001package de.deepamehta.core.model;
002
003import de.deepamehta.core.util.DeepaMehtaUtils;
004import de.deepamehta.core.util.SequencedHashMap;
005
006import org.codehaus.jettison.json.JSONArray;
007import org.codehaus.jettison.json.JSONException;
008import org.codehaus.jettison.json.JSONObject;
009
010import java.util.ArrayList;
011import java.util.Collection;
012import java.util.List;
013import java.util.logging.Logger;
014
015
016
017public abstract class TypeModel extends TopicModel {
018
019    // ---------------------------------------------------------------------------------------------- Instance Variables
020
021    private String dataTypeUri;
022    private List<IndexMode> indexModes;
023    private SequencedHashMap<String, AssociationDefinitionModel> assocDefs; // is never null, may be empty
024    private List<String> labelConfig;                                       // is never null, may be empty
025    private ViewConfigurationModel viewConfig;                              // is never null
026
027    private Logger logger = Logger.getLogger(getClass().getName());
028
029    // ---------------------------------------------------------------------------------------------------- Constructors
030
031    public TypeModel(String uri, String topicTypeUri, SimpleValue value, String dataTypeUri) {
032        super(uri, topicTypeUri, value);
033        this.dataTypeUri = dataTypeUri;
034        this.indexModes = new ArrayList();
035        this.assocDefs = new SequencedHashMap();
036        this.labelConfig = new ArrayList();
037        this.viewConfig = new ViewConfigurationModel();
038    }
039
040    public TypeModel(TopicModel topic, String dataTypeUri, List<IndexMode> indexModes,
041                     List<AssociationDefinitionModel> assocDefs, List<String> labelConfig,
042                     ViewConfigurationModel viewConfig) {
043        super(topic);
044        this.dataTypeUri = dataTypeUri;
045        this.indexModes = indexModes;
046        this.assocDefs = new SequencedHashMap();
047        for (AssociationDefinitionModel assocDef : assocDefs) {
048            addAssocDef(assocDef);
049        }
050        this.labelConfig = labelConfig;
051        this.viewConfig = viewConfig;
052    }
053
054    public TypeModel(JSONObject typeModel) {
055        super(typeModel);
056        try {
057            this.dataTypeUri = typeModel.getString("data_type_uri");
058            this.indexModes = IndexMode.parse(typeModel);
059            this.assocDefs = new SequencedHashMap();
060            this.labelConfig = parseLabelConfig(typeModel);
061            this.viewConfig = new ViewConfigurationModel(typeModel);
062            parseAssocDefs(typeModel);
063        } catch (Exception e) {
064            throw new RuntimeException("Parsing TypeModel failed (JSONObject=" + typeModel + ")", e);
065        }
066    }
067
068    // -------------------------------------------------------------------------------------------------- Public Methods
069
070    // === Data Type ===
071
072    public String getDataTypeUri() {
073        return dataTypeUri;
074    }
075
076    public void setDataTypeUri(String dataTypeUri) {
077        this.dataTypeUri = dataTypeUri;
078    }
079
080    // === Index Modes ===
081
082    public List<IndexMode> getIndexModes() {
083        return indexModes;
084    }
085
086    public void addIndexMode(IndexMode indexMode) {
087        indexModes.add(indexMode);
088    }
089
090    // === Association Definitions ===
091
092    public Collection<AssociationDefinitionModel> getAssocDefs() {
093        return assocDefs.values();
094    }
095
096    public AssociationDefinitionModel getAssocDef(String childTypeUri) {
097        AssociationDefinitionModel assocDef = assocDefs.get(childTypeUri);
098        if (assocDef == null) {
099            throw new RuntimeException("Schema violation: association definition \"" +
100                childTypeUri + "\" not found in " + this);
101        }
102        return assocDef;
103    }
104
105    public TypeModel addAssocDef(AssociationDefinitionModel assocDef) {
106        return addAssocDefBefore(assocDef, null);   // beforeChildTypeUri=null
107    }
108
109    /**
110     * @param   beforeChildTypeUri  the assoc def <i>before</i> the assoc def is inserted into the sequence.
111     *                              If <code>null</code> the assoc def is appended at the end.
112     */
113    public TypeModel addAssocDefBefore(AssociationDefinitionModel assocDef, String beforeChildTypeUri) {
114        // error check
115        String childTypeUri = assocDef.getChildTypeUri();
116        AssociationDefinitionModel existing = assocDefs.get(childTypeUri);
117        if (existing != null) {
118            throw new RuntimeException("Schema ambiguity: topic type \"" + uri +
119                "\" has more than one association definitions with uri \"" + childTypeUri + "\"");
120        }
121        //
122        assocDefs.putBefore(assocDef.getChildTypeUri(), assocDef, beforeChildTypeUri);
123        return this;
124    }
125
126    public void updateAssocDef(AssociationDefinitionModel assocDef) {
127        assocDefs.put(assocDef.getChildTypeUri(), assocDef);
128    }
129
130    public AssociationDefinitionModel removeAssocDef(String childTypeUri) {
131        // error check
132        getAssocDef(childTypeUri);
133        //
134        return assocDefs.remove(childTypeUri);
135    }
136
137    public void removeAllAssocDefs() {
138        assocDefs.clear();
139    }
140
141    // === Label Configuration ===
142
143    public List<String> getLabelConfig() {
144        return labelConfig;
145    }
146
147    public void setLabelConfig(List<String> labelConfig) {
148        this.labelConfig = labelConfig;
149    }
150
151    // === View Configuration ===
152
153    public ViewConfigurationModel getViewConfigModel() {
154        return viewConfig;
155    }
156
157    // FIXME: server-side operations on the view config settings possibly suggest they are not acually
158    // view config settings but part of the topic type model. Possibly this method should be dropped.
159    public Object getViewConfig(String typeUri, String settingUri) {
160        return viewConfig.getSetting(typeUri, settingUri);
161    }
162
163    public void setViewConfig(ViewConfigurationModel viewConfig) {
164        this.viewConfig = viewConfig;
165    }
166
167
168
169    // ****************************
170    // *** TopicModel Overrides ***
171    // ****************************
172
173
174
175    @Override
176    public JSONObject toJSON() {
177        try {
178            JSONObject o = super.toJSON();
179            //
180            o.put("data_type_uri", getDataTypeUri());
181            IndexMode.toJSON(indexModes, o);
182            AssociationDefinitionModel.toJSON(assocDefs.values(), o);
183            o.put("label_config", new JSONArray(getLabelConfig()));
184            getViewConfigModel().toJSON(o);
185            //
186            return o;
187        } catch (Exception e) {
188            throw new RuntimeException("Serialization failed (" + this + ")", e);
189        }
190    }
191
192
193
194    // ****************
195    // *** Java API ***
196    // ****************
197
198
199
200    @Override
201    public String toString() {
202        return "id=" + id + ", uri=\"" + uri + "\", value=\"" + value + "\", typeUri=\"" + typeUri +
203            "\", dataTypeUri=\"" + getDataTypeUri() + "\", indexModes=" + getIndexModes() + ", assocDefs=" +
204            getAssocDefs() + ", labelConfig=" + getLabelConfig() + ", " + getViewConfigModel();
205    }
206
207
208
209    // ------------------------------------------------------------------------------------------------- Private Methods
210
211    private List<String> parseLabelConfig(JSONObject typeModel) throws JSONException {
212        if (typeModel.has("label_config")) {
213            return DeepaMehtaUtils.toList(typeModel.getJSONArray("label_config"));
214        }
215        return new ArrayList();
216    }
217
218    private void parseAssocDefs(JSONObject typeModel) throws JSONException {
219        JSONArray assocDefs = typeModel.optJSONArray("assoc_defs");
220        if (assocDefs != null) {
221            for (int i = 0; i < assocDefs.length(); i++) {
222                JSONObject assocDef = assocDefs.getJSONObject(i);
223                assocDef.put("parent_type_uri", this.uri);
224                addAssocDef(parseAssocDef(assocDef));
225            }
226        }
227    }
228
229    private AssociationDefinitionModel parseAssocDef(JSONObject assocDef) {
230        try {
231            return new AssociationDefinitionModel(assocDef);
232        } catch (JSONException e) {
233            throw new RuntimeException("Parsing AssociationDefinitionModel failed (JSONObject=" + assocDef + ")", e);
234        }
235    }
236}