001package de.deepamehta.core.model;
002
003import org.codehaus.jettison.json.JSONException;
004import org.codehaus.jettison.json.JSONObject;
005
006import java.util.ArrayList;
007import java.util.Collection;
008import java.util.List;
009import java.util.Map;
010import 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 */
021public 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}