001    package de.deepamehta.core.model;
002    
003    import org.codehaus.jettison.json.JSONException;
004    import org.codehaus.jettison.json.JSONObject;
005    
006    import java.util.ArrayList;
007    import java.util.Collection;
008    import java.util.List;
009    import java.util.Map;
010    import java.util.logging.Logger;
011    
012    
013    
014    /**
015     * Definition of an association between 2 topic types -- part of DeepaMehta's type system,
016     * like an association in a class diagram. Used to represent both, aggregations and compositions.
017     * ### FIXDOC: also assoc types have assoc defs
018     *
019     * @author <a href="mailto:jri@deepamehta.de">Jörg Richter</a>
020     */
021    public class AssociationDefinitionModel extends AssociationModel {
022    
023        // ---------------------------------------------------------------------------------------------- Instance Variables
024    
025        private String customAssocTypeUri;
026    
027        private String parentTypeUri;   // derived, not serialized
028        private String childTypeUri;    // derived, not serialized
029    
030        private String parentCardinalityUri;
031        private String childCardinalityUri;
032    
033        private ViewConfigurationModel viewConfigModel; // is never null
034    
035        private Logger logger = Logger.getLogger(getClass().getName());
036    
037        // ---------------------------------------------------------------------------------------------------- Constructors
038    
039        public AssociationDefinitionModel(String assocTypeUri, String parentTypeUri, String childTypeUri,
040                                                               String parentCardinalityUri, String childCardinalityUri) {
041            this(assocTypeUri, null, parentTypeUri, childTypeUri, parentCardinalityUri, childCardinalityUri);
042        }
043    
044        public AssociationDefinitionModel(String assocTypeUri, String customAssocTypeUri,
045                                                               String parentTypeUri, String childTypeUri,
046                                                               String parentCardinalityUri, String childCardinalityUri) {
047            this(-1, null, assocTypeUri, customAssocTypeUri, parentTypeUri, childTypeUri, parentCardinalityUri,
048                childCardinalityUri, null);
049        }
050    
051        /**
052         * @param   customAssocTypeUri      if null no custom association type will be set.
053         */
054        public AssociationDefinitionModel(long id, String uri, String assocTypeUri, String customAssocTypeUri,
055                                                               String parentTypeUri, String childTypeUri,
056                                                               String parentCardinalityUri, String childCardinalityUri,
057                                                               ViewConfigurationModel viewConfigModel) {
058            super(id, uri, assocTypeUri, parentRoleModel(parentTypeUri), childRoleModel(childTypeUri));
059            //
060            this.customAssocTypeUri = customAssocTypeUri;
061            //
062            this.parentTypeUri = parentTypeUri;
063            this.childTypeUri = childTypeUri;
064            //
065            this.parentCardinalityUri = parentCardinalityUri;
066            this.childCardinalityUri = childCardinalityUri;
067            //
068            this.viewConfigModel = viewConfigModel != null ? viewConfigModel : new ViewConfigurationModel();
069        }
070    
071        AssociationDefinitionModel(JSONObject assocDef) throws JSONException {
072            super(assocDef.optLong("id", -1), null, assocDef.getString("assoc_type_uri"), parentRoleModel(assocDef),
073                                                                                          childRoleModel(assocDef));
074            // Note: getString() called on a key with JSON null value would return the string "null"
075            this.customAssocTypeUri = assocDef.isNull("custom_assoc_type_uri") ? null :
076                assocDef.getString("custom_assoc_type_uri");
077            //
078            this.parentTypeUri = parentTypeUri();
079            this.childTypeUri = childTypeUri();
080            //
081            if (!assocDef.has("parent_cardinality_uri") && !typeUri.equals("dm4.core.composition_def")) {
082                throw new RuntimeException("\"parent_cardinality_uri\" is missing");
083            }
084            this.parentCardinalityUri = assocDef.optString("parent_cardinality_uri", "dm4.core.one");
085            this.childCardinalityUri  = assocDef.getString("child_cardinality_uri");
086            //
087            this.viewConfigModel = new ViewConfigurationModel(assocDef);
088        }
089    
090        // -------------------------------------------------------------------------------------------------- Public Methods
091    
092        public String getCustomAssocTypeUri() {
093            return customAssocTypeUri;
094        }
095    
096        /**
097         * The type to be used to create an association instance based on this association definition.
098         */
099        public String getInstanceLevelAssocTypeUri() {
100            return customAssocTypeUri !=null ? customAssocTypeUri : defaultInstanceLevelAssocTypeUri();
101        }
102    
103        public String getParentTypeUri() {
104            return parentTypeUri;
105        }
106    
107        public String getChildTypeUri() {
108            return childTypeUri;
109        }
110    
111        public String getParentCardinalityUri() {
112            return parentCardinalityUri;
113        }
114    
115        public String getChildCardinalityUri() {
116            return childCardinalityUri;
117        }
118    
119        public ViewConfigurationModel getViewConfigModel() {
120            return viewConfigModel;
121        }
122    
123        // ---
124    
125        public void setCustomAssocTypeUri(String customAssocTypeUri) {
126            this.customAssocTypeUri = customAssocTypeUri;
127        }
128    
129        public void setParentCardinalityUri(String parentCardinalityUri) {
130            this.parentCardinalityUri = parentCardinalityUri;
131        }
132    
133        public void setChildCardinalityUri(String childCardinalityUri) {
134            this.childCardinalityUri = childCardinalityUri;
135        }
136    
137        public void setViewConfigModel(ViewConfigurationModel viewConfigModel) {
138            this.viewConfigModel = viewConfigModel;
139        }
140    
141        // ---
142    
143        public JSONObject toJSON() {
144            try {
145                JSONObject o = super.toJSON();
146                o.put("parent_cardinality_uri", parentCardinalityUri);
147                o.put("child_cardinality_uri", childCardinalityUri);
148                o.put("custom_assoc_type_uri", customAssocTypeUri != null ? customAssocTypeUri : JSONObject.NULL);
149                viewConfigModel.toJSON(o);
150                return o;
151            } catch (Exception e) {
152                throw new RuntimeException("Serialization failed (" + this + ")", e);
153            }
154        }
155    
156        // ---
157    
158        @Override
159        public String toString() {
160            return "\n    association definition (" + super.toString() +
161                ",\n        parent cardinality=\"" + parentCardinalityUri +
162                "\",\n        child cardinality=\"" + childCardinalityUri +
163                "\",\n        custom association type=\"" + customAssocTypeUri +
164                "\",\n        " + viewConfigModel + ")\n";
165        }
166    
167        // ----------------------------------------------------------------------------------------- Package Private Methods
168    
169        static void toJSON(Collection<AssociationDefinitionModel> assocDefs, JSONObject o) throws Exception {
170            List assocDefList = new ArrayList();
171            for (AssociationDefinitionModel assocDef : assocDefs) {
172                assocDefList.add(assocDef.toJSON());
173            }
174            o.put("assoc_defs", assocDefList);
175        }
176    
177        // ------------------------------------------------------------------------------------------------- Private Methods
178    
179        private static TopicRoleModel parentRoleModel(JSONObject assocDef) throws JSONException {
180            return parentRoleModel(assocDef.getString("parent_type_uri"));
181        }
182    
183        private static TopicRoleModel childRoleModel(JSONObject assocDef) throws JSONException {
184            return childRoleModel(assocDef.getString("child_type_uri"));
185        }
186    
187        // ---
188    
189        private static TopicRoleModel parentRoleModel(String parentTypeUri) {
190            return new TopicRoleModel(parentTypeUri, "dm4.core.parent_type");
191        }
192    
193        private static TopicRoleModel childRoleModel(String childTypeUri) {
194            return new TopicRoleModel(childTypeUri, "dm4.core.child_type");
195        }
196    
197        // ---
198    
199        private String parentTypeUri() {
200            return ((TopicRoleModel) getRoleModel("dm4.core.parent_type")).getTopicUri();
201        }
202    
203        private String childTypeUri() {
204            return ((TopicRoleModel) getRoleModel("dm4.core.child_type")).getTopicUri();
205        }
206    
207        // ---
208    
209        private String defaultInstanceLevelAssocTypeUri() {
210            if (typeUri.equals("dm4.core.aggregation_def")) {
211                return "dm4.core.aggregation";
212            } else if (typeUri.equals("dm4.core.composition_def")) {
213                return "dm4.core.composition";
214            } else {
215                throw new RuntimeException("Unexpected association type URI: \"" + typeUri + "\"");
216            }
217        }
218    }