001    package de.deepamehta.core.model;
002    
003    import de.deepamehta.core.util.DeepaMehtaUtils;
004    import de.deepamehta.core.util.SequencedHashMap;
005    
006    import org.codehaus.jettison.json.JSONArray;
007    import org.codehaus.jettison.json.JSONException;
008    import org.codehaus.jettison.json.JSONObject;
009    
010    import java.util.ArrayList;
011    import java.util.Collection;
012    import java.util.List;
013    import java.util.logging.Logger;
014    
015    
016    
017    public 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", DeepaMehtaUtils.stringsToJson(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    }