001    package de.deepamehta.core.model;
002    
003    import de.deepamehta.core.util.DeepaMehtaUtils;
004    
005    import org.codehaus.jettison.json.JSONObject;
006    import org.codehaus.jettison.json.JSONArray;
007    
008    import java.util.ArrayList;
009    import java.util.Collection;
010    import java.util.LinkedHashMap;
011    import java.util.List;
012    import java.util.Map;
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 Map<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 LinkedHashMap();
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 LinkedHashMap();
047            for (AssociationDefinitionModel assocDef : assocDefs) {
048                addAssocDef(assocDef);
049            }
050            this.labelConfig = labelConfig;
051            this.viewConfig = viewConfig;
052        }
053    
054        public TypeModel(JSONObject typeModel, String typeUri) {
055            super(typeModel, typeUri);
056            try {
057                this.dataTypeUri = typeModel.getString("data_type_uri");
058                this.indexModes = IndexMode.parse(typeModel);
059                this.assocDefs = new LinkedHashMap();
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            // error check ### FIXME: drop this check or provide proper feedback to the type editor user
107            if (!getDataTypeUri().equals("dm4.core.composite")) {
108                throw new RuntimeException("Association definitions can only be added to composite topic types. " +
109                    "Topic type \"" + getUri() + "\" is of data type \"" + getDataTypeUri() + "\". (" + assocDef + ")");
110            }
111            // error check
112            String childTypeUri = assocDef.getChildTypeUri();
113            AssociationDefinitionModel existing = assocDefs.get(childTypeUri);
114            if (existing != null) {
115                throw new RuntimeException("Schema ambiguity: topic type \"" + uri +
116                    "\" has more than one association definitions with uri \"" + childTypeUri + "\"");
117            }
118            //
119            updateAssocDef(assocDef);
120            //
121            return this;
122        }
123    
124        public void updateAssocDef(AssociationDefinitionModel assocDef) {
125            assocDefs.put(assocDef.getChildTypeUri(), assocDef);
126        }
127    
128        public AssociationDefinitionModel removeAssocDef(String childTypeUri) {
129            // error check
130            getAssocDef(childTypeUri);
131            //
132            return assocDefs.remove(childTypeUri);
133        }
134    
135        public void removeAllAssocDefs() {
136            assocDefs.clear();
137        }
138    
139        // === Label Configuration ===
140    
141        public List<String> getLabelConfig() {
142            return labelConfig;
143        }
144    
145        public void setLabelConfig(List<String> labelConfig) {
146            this.labelConfig = labelConfig;
147        }
148    
149        // === View Configuration ===
150    
151        public ViewConfigurationModel getViewConfigModel() {
152            return viewConfig;
153        }
154    
155        // FIXME: server-side operations on the view config settings possibly suggest they are not acually
156        // view config settings but part of the topic type model. Possibly this method should be dropped.
157        public Object getViewConfig(String typeUri, String settingUri) {
158            return viewConfig.getSetting(typeUri, settingUri);
159        }
160    
161        public void setViewConfig(ViewConfigurationModel viewConfig) {
162            this.viewConfig = viewConfig;
163        }
164    
165    
166    
167        // ****************************
168        // *** TopicModel Overrides ***
169        // ****************************
170    
171    
172    
173        @Override
174        public JSONObject toJSON() {
175            try {
176                JSONObject o = super.toJSON();
177                //
178                o.put("data_type_uri", getDataTypeUri());
179                IndexMode.toJSON(indexModes, o);
180                AssociationDefinitionModel.toJSON(assocDefs.values(), o);
181                o.put("label_config", DeepaMehtaUtils.stringsToJson(getLabelConfig()));
182                getViewConfigModel().toJSON(o);
183                //
184                return o;
185            } catch (Exception e) {
186                throw new RuntimeException("Serialization failed (" + this + ")", e);
187            }
188        }
189    
190    
191    
192        // ****************
193        // *** Java API ***
194        // ****************
195    
196    
197    
198        @Override
199        public String toString() {
200            return "id=" + id + ", uri=\"" + uri + "\", value=\"" + value + "\", typeUri=\"" + typeUri +
201                "\", dataTypeUri=\"" + getDataTypeUri() + "\", indexModes=" + getIndexModes() + ", assocDefs=" +
202                getAssocDefs() + ", labelConfig=" + getLabelConfig() + ", " + getViewConfigModel();
203        }
204    
205    
206    
207        // ------------------------------------------------------------------------------------------------- Private Methods
208    
209        private List<String> parseLabelConfig(JSONObject typeModel) throws Exception {
210            if (typeModel.has("label_config")) {
211                return DeepaMehtaUtils.toList(typeModel.getJSONArray("label_config"));
212            }
213            return new ArrayList();
214        }
215    
216        private void parseAssocDefs(JSONObject typeModel) throws Exception {
217            JSONArray assocDefs = typeModel.optJSONArray("assoc_defs");
218            if (assocDefs != null) {
219                for (int i = 0; i < assocDefs.length(); i++) {
220                    JSONObject assocDef = assocDefs.getJSONObject(i);
221                    addAssocDef(AssociationDefinitionModel.fromJSON(assocDef, this.uri));
222                }
223            }
224        }
225    }