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 void 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    
122        public void updateAssocDef(AssociationDefinitionModel assocDef) {
123            assocDefs.put(assocDef.getChildTypeUri(), assocDef);
124        }
125    
126        public AssociationDefinitionModel removeAssocDef(String childTypeUri) {
127            // error check
128            getAssocDef(childTypeUri);
129            //
130            return assocDefs.remove(childTypeUri);
131        }
132    
133        public void removeAllAssocDefs() {
134            assocDefs.clear();
135        }
136    
137        // === Label Configuration ===
138    
139        public List<String> getLabelConfig() {
140            return labelConfig;
141        }
142    
143        public void setLabelConfig(List<String> labelConfig) {
144            this.labelConfig = labelConfig;
145        }
146    
147        // === View Configuration ===
148    
149        public ViewConfigurationModel getViewConfigModel() {
150            return viewConfig;
151        }
152    
153        // FIXME: server-side operations on the view config settings possibly suggest they are not acually
154        // view config settings but part of the topic type model. Possibly this method should be dropped.
155        public Object getViewConfig(String typeUri, String settingUri) {
156            return viewConfig.getSetting(typeUri, settingUri);
157        }
158    
159        public void setViewConfig(ViewConfigurationModel viewConfig) {
160            this.viewConfig = viewConfig;
161        }
162    
163    
164    
165        // ****************************
166        // *** TopicModel Overrides ***
167        // ****************************
168    
169    
170    
171        @Override
172        public JSONObject toJSON() {
173            try {
174                JSONObject o = super.toJSON();
175                //
176                o.put("data_type_uri", getDataTypeUri());
177                IndexMode.toJSON(indexModes, o);
178                AssociationDefinitionModel.toJSON(assocDefs.values(), o);
179                o.put("label_config", DeepaMehtaUtils.stringsToJson(getLabelConfig()));
180                getViewConfigModel().toJSON(o);
181                //
182                return o;
183            } catch (Exception e) {
184                throw new RuntimeException("Serialization failed (" + this + ")", e);
185            }
186        }
187    
188    
189    
190        // ****************
191        // *** Java API ***
192        // ****************
193    
194    
195    
196        @Override
197        public String toString() {
198            return "id=" + id + ", uri=\"" + uri + "\", value=\"" + value + "\", typeUri=\"" + typeUri +
199                "\", dataTypeUri=\"" + getDataTypeUri() + "\", indexModes=" + getIndexModes() + ", assocDefs=" +
200                getAssocDefs() + ", labelConfig=" + getLabelConfig() + ", " + getViewConfigModel();
201        }
202    
203    
204    
205        // ------------------------------------------------------------------------------------------------- Private Methods
206    
207        private List<String> parseLabelConfig(JSONObject typeModel) throws Exception {
208            if (typeModel.has("label_config")) {
209                return DeepaMehtaUtils.toList(typeModel.getJSONArray("label_config"));
210            }
211            return new ArrayList();
212        }
213    
214        private void parseAssocDefs(JSONObject typeModel) throws Exception {
215            JSONArray assocDefs = typeModel.optJSONArray("assoc_defs");
216            if (assocDefs != null) {
217                for (int i = 0; i < assocDefs.length(); i++) {
218                    JSONObject assocDef = assocDefs.getJSONObject(i);
219                    addAssocDef(AssociationDefinitionModel.fromJSON(assocDef, this.uri));
220                }
221            }
222        }
223    }