001package systems.dmx.core.util;
002
003import systems.dmx.core.DMXObject;
004import systems.dmx.core.Identifiable;
005import systems.dmx.core.JSONEnabled;
006import systems.dmx.core.Topic;
007import systems.dmx.core.model.AssociationModel;
008import systems.dmx.core.model.RoleModel;
009import systems.dmx.core.model.TopicModel;
010import systems.dmx.core.model.TopicRoleModel;
011import systems.dmx.core.service.CoreService;
012
013import org.codehaus.jettison.json.JSONArray;
014import org.codehaus.jettison.json.JSONObject;
015
016import java.net.URL;
017import java.util.ArrayList;
018import java.util.Collection;
019import java.util.HashMap;
020import java.util.Iterator;
021import java.util.List;
022import java.util.Map;
023import java.util.logging.Logger;
024
025
026
027public class DMXUtils {
028
029    private static final Logger logger = Logger.getLogger(DMXUtils.class.getName());
030
031    private static final String DM4_HOST_URL = System.getProperty("dmx.host.url");  // ### TODO: default value (#734)
032    static {
033        logger.info("Host setting:\ndmx.host.url=\"" + DM4_HOST_URL + "\"");
034    }
035
036
037
038    // ************
039    // *** URLs ***
040    // ************
041
042
043
044    /**
045     * Checks if an URL refers to this DMX installation.
046     * The check relies on the "dmx.host.url" system property.
047     */
048    public static boolean isDMXURL(URL url) {
049        try {
050            return url.toString().startsWith(DM4_HOST_URL);
051        } catch (Exception e) {
052            throw new RuntimeException("Checking for DMX URL failed (url=\"" + url + "\")", e);
053        }
054    }
055
056
057
058    // *******************
059    // *** Collections ***
060    // *******************
061
062
063
064    public static List<Long> idList(Iterable<? extends Identifiable> items) {
065        List<Long> ids = new ArrayList();
066        for (Identifiable item : items) {
067            ids.add(item.getId());
068        }
069        return ids;
070    }
071
072    public static <M> List<M> toModelList(Iterable<? extends DMXObject> objects) {
073        List<M> modelList = new ArrayList();
074        for (DMXObject object : objects) {
075            modelList.add((M) object.getModel());
076        }
077        return modelList;
078    }
079
080    public static String topicNames(Iterable<? extends Topic> topics) {
081        StringBuilder names = new StringBuilder();
082        Iterator<? extends Topic> i = topics.iterator();
083        while (i.hasNext()) {
084            Topic topic = i.next();
085            names.append('"').append(topic.getSimpleValue()).append('"');
086            if (i.hasNext()) {
087                names.append(", ");
088            }
089        }
090        return names.toString();
091    }
092
093    public static <T extends DMXObject> List<T> loadChildTopics(List<T> objects) {
094        for (DMXObject object : objects) {
095            object.loadChildTopics();
096        }
097        return objects;
098    }
099
100
101
102    // ************
103    // *** JSON ***
104    // ************
105
106
107
108    // === Generic ===
109
110    public static Map toMap(JSONObject o) {
111        return toMap(o, new HashMap());
112    }
113
114    public static Map toMap(JSONObject o, Map map) {
115        try {
116            Iterator<String> i = o.keys();
117            while (i.hasNext()) {
118                String key = i.next();
119                map.put(key, o.get(key));   // throws JSONException
120            }
121            return map;
122        } catch (Exception e) {
123            throw new RuntimeException("Converting JSONObject to Map failed", e);
124        }
125    }
126
127    // ---
128
129    public static List toList(JSONArray o) {
130        try {
131            List list = new ArrayList();
132            for (int i = 0; i < o.length(); i++) {
133                list.add(o.get(i));         // throws JSONException
134            }
135            return list;
136        } catch (Exception e) {
137            throw new RuntimeException("Converting JSONArray to List failed", e);
138        }
139    }
140
141    // === DMX specific ===
142
143    public static JSONArray toJSONArray(Iterable<? extends JSONEnabled> items) {
144        JSONArray array = new JSONArray();
145        for (JSONEnabled item : items) {
146            array.put(item.toJSON());
147        }
148        return array;
149    }
150
151
152
153    // *******************************
154    // *** Association Auto-Typing ***
155    // *******************************
156
157
158
159    /**
160     * Retypes the given association if its player types match the given topic types.
161     */
162    public static RoleModel[] associationAutoTyping(AssociationModel assoc, String topicTypeUri1, String topicTypeUri2,
163                                       String assocTypeUri, String roleTypeUri1, String roleTypeUri2, CoreService dmx) {
164        if (!assoc.getTypeUri().equals("dmx.core.association")) {
165            return null;
166        }
167        RoleModel[] roles = getRoleModels(assoc, topicTypeUri1, topicTypeUri2, dmx);
168        if (roles != null) {
169            logger.info("### Auto typing association into \"" + assocTypeUri +
170                "\" (\"" + topicTypeUri1 + "\" <-> \"" + topicTypeUri2 + "\")");
171            assoc.setTypeUri(assocTypeUri);
172            roles[0].setRoleTypeUri(roleTypeUri1);
173            roles[1].setRoleTypeUri(roleTypeUri2);
174        }
175        return roles;
176    }
177
178    public static RoleModel[] getRoleModels(AssociationModel assoc, String topicTypeUri1, String topicTypeUri2,
179                                                                                          CoreService dmx) {
180        RoleModel r1 = assoc.getRoleModel1();
181        RoleModel r2 = assoc.getRoleModel2();
182        // ### FIXME: auto-typing is supported only for topic players, and if they are identified by-ID.
183        // Note: we can't call roleModel.getPlayer() as this would build an entire object model, but its "value"
184        // is not yet available in case this association is part of the player's composite structure.
185        // Compare to AssociationModelImpl.duplicateCheck()
186        if (!(r1 instanceof TopicRoleModel) || ((TopicRoleModel) r1).topicIdentifiedByUri() ||
187            !(r2 instanceof TopicRoleModel) || ((TopicRoleModel) r2).topicIdentifiedByUri()) {
188            return null;
189        }
190        String t1 = (String) dmx.getProperty(r1.getPlayerId(), "typeUri");
191        String t2 = (String) dmx.getProperty(r2.getPlayerId(), "typeUri");
192        RoleModel roleModel1 = getRoleModel(r1, r2, t1, t2, topicTypeUri1, 1);
193        RoleModel roleModel2 = getRoleModel(r1, r2, t1, t2, topicTypeUri2, 2);
194        if (roleModel1 != null && roleModel2 != null) {
195            return new RoleModel[] {roleModel1, roleModel2};
196        }
197        return null;
198    }
199
200    // ------------------------------------------------------------------------------------------------- Private Methods
201
202    private static RoleModel getRoleModel(RoleModel r1, RoleModel r2, String t1, String t2, String topicTypeUri,
203                                                                                            int nr) {
204        boolean m1 = t1.equals(topicTypeUri);
205        boolean m2 = t2.equals(topicTypeUri);
206        if (m1 && m2) {
207            return nr == 1 ? r1 : r2;
208        }
209        return m1 ? r1 : m2 ? r2 : null;
210    }
211}