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}