001package systems.dmx.core.impl;
002
003import systems.dmx.core.JSONEnabled;
004import systems.dmx.core.model.AssociationDefinitionModel;
005import systems.dmx.core.model.AssociationModel;
006import systems.dmx.core.model.DMXObjectModel;
007import systems.dmx.core.model.IndexMode;
008import systems.dmx.core.model.SimpleValue;
009import systems.dmx.core.model.TopicModel;
010import systems.dmx.core.model.TypeModel;
011import systems.dmx.core.model.ViewConfigurationModel;
012import systems.dmx.core.service.Directive;
013import systems.dmx.core.service.Directives;
014import systems.dmx.core.util.SequencedHashMap;
015
016import org.codehaus.jettison.json.JSONArray;
017import org.codehaus.jettison.json.JSONObject;
018
019import java.util.ArrayList;
020import java.util.Collection;
021import java.util.Iterator;
022import java.util.List;
023import java.util.logging.Logger;
024
025
026
027class TypeModelImpl extends TopicModelImpl implements TypeModel {
028
029    // ---------------------------------------------------------------------------------------------- Instance Variables
030
031    private String dataTypeUri;     // may be null in models used for an update operation
032    private List<IndexMode> indexModes;
033    private SequencedHashMap<String, AssociationDefinitionModelImpl> assocDefs; // is never null, may be empty
034    private ViewConfigurationModelImpl viewConfig;                              // is never null
035
036    private Logger logger = Logger.getLogger(getClass().getName());
037
038    // ---------------------------------------------------------------------------------------------------- Constructors
039
040    TypeModelImpl(TopicModelImpl typeTopic, String dataTypeUri, List<IndexMode> indexModes,
041                  List<AssociationDefinitionModel> assocDefs, ViewConfigurationModelImpl viewConfig) {
042        super(typeTopic);
043        this.dataTypeUri  = dataTypeUri;
044        this.indexModes   = indexModes;
045        this.assocDefs    = toMap(assocDefs);
046        this.viewConfig   = viewConfig;
047    }
048
049    TypeModelImpl(TypeModelImpl type) {
050        super(type);
051        this.dataTypeUri  = type.getDataTypeUri();
052        this.indexModes   = type.getIndexModes();
053        this.assocDefs    = toMap(type.getAssocDefs());
054        this.viewConfig   = type.getViewConfig();
055    }
056
057    // -------------------------------------------------------------------------------------------------- Public Methods
058
059
060
061    // === Data Type ===
062
063    @Override
064    public String getDataTypeUri() {
065        return dataTypeUri;
066    }
067
068    @Override
069    public void setDataTypeUri(String dataTypeUri) {
070        this.dataTypeUri = dataTypeUri;
071    }
072
073
074
075    // === Index Modes ===
076
077    @Override
078    public List<IndexMode> getIndexModes() {
079        return indexModes;
080    }
081
082    @Override
083    public void addIndexMode(IndexMode indexMode) {
084        indexModes.add(indexMode);
085    }
086
087
088
089    // === Association Definitions ===
090
091    @Override
092    public Collection<AssociationDefinitionModelImpl> getAssocDefs() {
093        return assocDefs.values();
094    }
095
096    @Override
097    public AssociationDefinitionModelImpl getAssocDef(String assocDefUri) {
098        return getAssocDefOrThrow(assocDefUri);
099    }
100
101    @Override
102    public boolean hasAssocDef(String assocDefUri) {
103        return _getAssocDef(assocDefUri) != null;
104    }
105
106    /**
107     * @param   assocDef    the assoc def to add.
108     *                      Note: its ID might be uninitialized (-1).
109     */
110    @Override
111    public TypeModel addAssocDef(AssociationDefinitionModel assocDef) {
112        return addAssocDefBefore(assocDef, null);   // beforeAssocDefUri=null
113    }
114
115    /**
116     * @param   assocDef            the assoc def to add.
117     *                              Note: its ID might be uninitialized (-1).
118     * @param   beforeAssocDefUri   the URI of the assoc def <i>before</i> the given assoc def is inserted.
119     *                              If <code>null</code> the assoc def is appended at the end.
120     */
121    @Override
122    public TypeModel addAssocDefBefore(AssociationDefinitionModel assocDef, String beforeAssocDefUri) {
123        try {
124            // error check
125            String assocDefUri = assocDef.getAssocDefUri();
126            AssociationDefinitionModel existing = _getAssocDef(assocDefUri);
127            if (existing != null) {
128                throw new RuntimeException("Ambiguity: type \"" + uri + "\" has more than one \"" + assocDefUri +
129                    "\" assoc defs");
130            }
131            //
132            assocDefs.putBefore(assocDefUri, (AssociationDefinitionModelImpl) assocDef, beforeAssocDefUri);
133            return this;
134        } catch (Exception e) {
135            throw new RuntimeException("Adding an assoc def to type \"" + uri + "\" before \"" + beforeAssocDefUri +
136                "\" failed" + assocDef, e);
137        }
138    }
139
140    @Override
141    public AssociationDefinitionModel removeAssocDef(String assocDefUri) {
142        try {
143            AssociationDefinitionModel assocDef = assocDefs.remove(assocDefUri);
144            if (assocDef == null) {
145                throw new RuntimeException("Assoc def \"" + assocDefUri + "\" not found in " + assocDefs.keySet());
146            }
147            return assocDef;
148        } catch (Exception e) {
149            throw new RuntimeException("Removing assoc def \"" + assocDefUri + "\" from type \"" + uri + "\" failed",
150                e);
151        }
152    }
153
154
155
156    // === View Configuration ===
157
158    @Override
159    public ViewConfigurationModelImpl getViewConfig() {
160        return viewConfig;
161    }
162
163    @Override
164    public Object getViewConfigValue(String configTypeUri, String childTypeUri) {
165        return viewConfig.getConfigValue(configTypeUri, childTypeUri);
166    }
167
168    @Override
169    public void setViewConfig(ViewConfigurationModel viewConfig) {
170        this.viewConfig = (ViewConfigurationModelImpl) viewConfig;
171    }
172
173
174
175    // === Iterable Implementation ===
176
177    /**
178     * Returns an interator which iterates this TypeModel's assoc def URIs.
179     */
180    @Override
181    public Iterator<String> iterator() {
182        return assocDefs.keySet().iterator();
183    }
184
185
186
187    // ****************************
188    // *** TopicModel Overrides ***
189    // ****************************
190
191
192
193    @Override
194    public JSONObject toJSON() {
195        try {
196            return super.toJSON()
197                .put("dataTypeUri", dataTypeUri)
198                .put("indexModeUris", toJSONArray(indexModes))
199                .put("assocDefs", toJSONArray(assocDefs.values()))
200                .put("viewConfigTopics", viewConfig.toJSONArray());
201        } catch (Exception e) {
202            throw new RuntimeException("Serialization failed", e);
203        }
204    }
205
206
207
208    // ****************
209    // *** Java API ***
210    // ****************
211
212
213
214    @Override
215    public TypeModelImpl clone() {
216        try {
217            TypeModelImpl model = (TypeModelImpl) super.clone();
218            model.assocDefs = (SequencedHashMap) model.assocDefs.clone();
219            return model;
220        } catch (Exception e) {
221            throw new RuntimeException("Cloning a TypeModel failed", e);
222        }
223    }
224
225    // ----------------------------------------------------------------------------------------- Package Private Methods
226
227
228
229    // === Abstract Methods ===
230
231    DMXTypeImpl instantiate() {
232        throw new UnsupportedOperationException();
233    }
234
235    List<? extends DMXObjectModelImpl> getAllInstances() {
236        throw new UnsupportedOperationException();
237    }
238
239    // ---
240
241    Directive getUpdateTypeDirective() {
242        throw new UnsupportedOperationException();
243    }
244
245    Directive getDeleteTypeDirective() {
246        throw new UnsupportedOperationException();
247    }
248
249
250
251    // === Core Internal Hooks ===
252
253    @Override
254    void preUpdate(DMXObjectModel updateModel) {
255        // ### TODO: is it sufficient if we rehash (remove + add) at post-time?
256        if (uriChange(updateModel.getUri(), uri)) {
257            removeFromTypeCache();
258        }
259    }
260
261    @Override
262    void postUpdate(DMXObjectModel updateModel, DMXObjectModel oldObject) {
263        if (uriChange(updateModel.getUri(), oldObject.getUri())) {
264            putInTypeCache();
265        }
266        //
267        updateType((TypeModelImpl) updateModel);
268        //
269        // Note: the UPDATE_TOPIC_TYPE/UPDATE_ASSOCIATION_TYPE directive must be added *before* a possible UPDATE_TOPIC
270        // directive (added by DMXObjectModelImpl.update()). In case of a changed type URI the webclient's type
271        // cache must be updated *before* the TopicTypeRenderer/AssociationTypeRenderer can render the type.
272        addUpdateTypeDirective();
273    }
274
275    // ---
276
277    @Override
278    void preDelete() {
279        // 1) check pre-condition
280        int size = getAllInstances().size();
281        if (size > 0) {
282            throw new RuntimeException(size + " \"" + value + "\" instances still exist");
283        }
284        // 2) delete all assoc defs
285        //
286        // Note 1: we use the preDelete() hook to delete the assoc defs *before* they would get deleted by the generic
287        // deletion logic (see "delete direct associations" in DMXObjectModelImpl.delete()). The generic deletion
288        // logic deletes the direct associations in an *arbitrary* order. The "Sequence Start" association might get
289        // deleted *before* any other assoc def. When subsequently deleting an assoc def the sequence can't be rebuild
290        // as it is corrupted.
291        // Note 2: iterating with a for-loop here would cause ConcurrentModificationException. Deleting an assoc def
292        // implies rebuilding the sequence and that iterates with a for-loop already. Instead we must create a new
293        // iterator for every single assoc def.
294        String assocDefUri;
295        while ((assocDefUri = getFirstAssocDefUri()) != null) {
296            _getAssocDef(assocDefUri).delete();
297        }
298    }
299
300    @Override
301    void postDelete() {
302        super.postDelete();     // ### TODO: needed?
303        removeFromTypeCache();
304    }
305
306
307
308    // === Update (memory + DB) ===
309
310    final void updateDataTypeUri(String dataTypeUri) {
311        setDataTypeUri(dataTypeUri);    // update memory
312        storeDataTypeUri();             // update DB
313    }
314
315    final void _addIndexMode(IndexMode indexMode) {
316        // update memory
317        addIndexMode(indexMode);
318        // update DB
319        pl.typeStorage.storeIndexMode(uri, indexMode);
320        indexAllInstances(indexMode);
321    }
322
323    // ---
324
325    final void _addAssocDefBefore(AssociationDefinitionModelImpl assocDef, String beforeAssocDefUri) {
326        try {
327            long lastAssocDefId = lastAssocDefId();     // must be determined *before* memory is updated
328            //
329            // 1) update memory
330            // Note: the assoc def's custom association type is stored as a child topic. The meta model extension that
331            // adds "Association Type" as a child to the "Composition Definition" and "Aggregation Definition"
332            // association types has itself a custom association type (named "Custom Association Type"), see migration
333            // 5. It would not be stored as storage is model driven and the (meta) model doesn't know about custom
334            // associations as this very concept is introduced only by the assoc def being added here. So, the model
335            // must be updated (in-memory) *before* the assoc def is stored.
336            addAssocDefBefore(assocDef, beforeAssocDefUri);
337            //
338            // 2) update DB
339            pl.typeStorage.storeAssociationDefinition(assocDef);
340            long beforeAssocDefId = beforeAssocDefUri != null ? getAssocDef(beforeAssocDefUri).getId() : -1;
341            long firstAssocDefId = firstAssocDefId();   // must be determined *after* memory is updated
342            pl.typeStorage.addAssocDefToSequence(getId(), assocDef.getId(), beforeAssocDefId, firstAssocDefId,
343                lastAssocDefId);
344        } catch (Exception e) {
345            throw new RuntimeException("Adding an assoc def to type \"" + uri + "\" before \"" + beforeAssocDefUri +
346                "\" failed" + assocDef, e);
347        }
348    }
349
350    final void _removeAssocDef(String assocDefUri) {
351        // We trigger deleting an association definition by deleting the underlying association. This mimics deleting an
352        // association definition interactively in the webclient. Updating this type definition's memory and DB sequence
353        // is triggered then by the Type Editor plugin's preDeleteAssociation() hook. ### FIXDOC
354        // This way deleting an association definition works for both cases: 1) interactive deletion (when the user
355        // deletes an association), and 2) programmatical deletion (e.g. from a migration).
356        getAssocDef(assocDefUri).delete();
357    }
358
359
360
361    // === Type Editor Support ===
362
363    final void _addAssocDef(AssociationModelImpl assoc) {
364        _addAssocDefBefore(pl.typeStorage.newAssociationDefinition(assoc), null);    // beforeAssocDefUri=null
365        //
366        addUpdateTypeDirective();
367    }
368
369    /**
370     * Adjusts the type cache on post-update-assoc.
371     * Called from ApplicationModel's core internal postUpdate() hook, in 2 situations:
372     *   - An assoc def has been updated.
373     *   - An assoc def's underlying assoc has been updated.
374     *
375     * @param   assoc       the updated generic association. ### FIXDOC: might be an assoc def as well
376     */
377    final void _updateAssocDef(AssociationModel assoc, AssociationModel oldAssoc) {
378        String[] assocDefUris = findAssocDefUris(assoc.getId());
379        AssociationDefinitionModel assocDef = getAssocDef(assocDefUris[0]);
380        String oldAssocDefUri;
381        if (assoc == assocDef) {
382            // updated "via assoc def"
383            oldAssocDefUri = ((AssociationDefinitionModel) oldAssoc).getAssocDefUri();
384        } else {
385            // updated "via assoc"
386            oldAssocDefUri = assocDef.getAssocDefUri();
387            // transfer assoc model to assoc def model
388            assocDef.setTypeUri(assoc.getTypeUri());
389            assocDef.setSimpleValue(assoc.getSimpleValue());
390            assocDef.setChildTopicsModel(assoc.getChildTopicsModel());
391        }
392        // rehash if assoc def URI has changed (due to changed custom assoc type)
393        if (!assocDef.getAssocDefUri().equals(oldAssocDefUri)) {
394            rehashAssocDef(oldAssocDefUri, assocDefUris[1]);
395        }
396        //
397        addUpdateTypeDirective();
398    }
399
400    /**
401     * Removes an association from memory and rebuilds the sequence in DB. Note: the underlying
402     * association is *not* removed from DB.
403     * This method is called (by the Type Editor plugin's preDeleteAssociation() hook) when the
404     * deletion of an association that represents an association definition is imminent. ### FIXDOC
405     */
406    final void _removeAssocDefFromMemoryAndRebuildSequence(AssociationModel assoc) {
407        String[] assocDefUris = findAssocDefUris(assoc.getId());
408        String assocDefUri = getAssocDef(assocDefUris[0]).getAssocDefUri();
409        // update memory
410        removeAssocDef(assocDefUri);
411        // update DB
412        pl.typeStorage.rebuildSequence(this);
413        //
414        addUpdateTypeDirective();
415    }
416
417
418
419    // === Identity Configuration ===
420
421    final List<String> getIdentityAttrs() {
422        try {
423            List<String> identityAttrs = new ArrayList();
424            for (String assocDefUri : this) {
425                if (getAssocDef(assocDefUri).isIdentityAttr()) {
426                    identityAttrs.add(assocDefUri);
427                }
428            }
429            return identityAttrs;
430        } catch (Exception e) {
431            throw new RuntimeException("Calculating the identity configuration for type \"" + uri + "\" failed", e);
432        }
433    }
434
435
436
437    // === Label Configuration ===
438
439    /**
440     * Prerequisite: this is a composite type.
441     */
442    final List<String> getLabelAssocDefUris() {
443        List<String> labelConfig = getLabelConfig();
444        if (labelConfig.size() > 0) {
445            return labelConfig;
446        } else {
447            List<String> assocDefUris = new ArrayList();
448            Iterator<? extends AssociationDefinitionModel> i = getAssocDefs().iterator();
449            // Note: types just created might have no child types yet
450            if (i.hasNext()) {
451                assocDefUris.add(i.next().getAssocDefUri());
452            }
453            return assocDefUris;
454        }
455    }
456
457    final List<String> getLabelConfig() {
458        try {
459            List<String> labelConfig = new ArrayList();
460            for (String assocDefUri : this) {
461                if (getAssocDef(assocDefUri).includeInLabel()) {
462                    labelConfig.add(assocDefUri);
463                }
464            }
465            return labelConfig;
466        } catch (Exception e) {
467            throw new RuntimeException("Calculating the label configuration for type \"" + uri + "\" failed", e);
468        }
469    }
470
471
472
473    // === Access Control ===
474
475    final <M extends TypeModelImpl> M filterReadableAssocDefs() {
476        try {
477            Iterator<String> i = iterator();
478            while (i.hasNext()) {
479                String assocDefUri = i.next();
480                if (!_getAssocDef(assocDefUri).isReadable()) {
481                    i.remove();
482                }
483            }
484            return (M) this;
485        } catch (Exception e) {
486            throw new RuntimeException("Filtering readable assoc defs of type \"" + uri + "\" failed", e);
487        }
488    }
489
490
491
492    // ------------------------------------------------------------------------------------------------- Private Methods
493
494    private void addUpdateTypeDirective() {
495        Directives.get().add(getUpdateTypeDirective(), instantiate());
496    }
497
498
499
500    // === Update (memory + DB) ===
501
502    private void updateType(TypeModelImpl updateModel) {
503        _updateDataTypeUri(updateModel.getDataTypeUri());
504        _updateAssocDefs(updateModel.getAssocDefs());
505        _updateSequence(updateModel.getAssocDefs());
506    }
507
508    // ---
509
510    // TODO: the public object API setters should call these _update() methods (instead update())
511
512    private void _updateDataTypeUri(String newDataTypeUri) {
513        if (newDataTypeUri != null) {
514            String dataTypeUri = getDataTypeUri();
515            if (!dataTypeUri.equals(newDataTypeUri)) {
516                logger.info("### Changing data type URI: \"" + dataTypeUri + "\" -> \"" + newDataTypeUri + "\"");
517                updateDataTypeUri(newDataTypeUri);
518            }
519        }
520    }
521
522    private void _updateAssocDefs(Collection<AssociationDefinitionModelImpl> newAssocDefs) {
523        for (AssociationDefinitionModelImpl assocDef : newAssocDefs) {
524            // Note: if the assoc def's custom association type was changed the assoc def URI changes as well.
525            // So we must identify the assoc def to update **by ID**.
526            // ### TODO: drop updateAssocDef() and rehash here (that is remove + add).
527            String[] assocDefUris = findAssocDefUris(assocDef.getId());
528            getAssocDef(assocDefUris[0]).update(assocDef);
529        }
530    }
531
532    private void _updateSequence(Collection<AssociationDefinitionModelImpl> newAssocDefs) {
533        try {
534            // ### TODO: only update sequence if there is a change request
535            //
536            // update memory
537            logger.info("##### Updating sequence (" + newAssocDefs.size() + "/" + getAssocDefs().size() +
538                " assoc defs)");
539            rehashAssocDefs(newAssocDefs);
540            // update DB
541            pl.typeStorage.rebuildSequence(this);
542        } catch (Exception e) {
543            throw new RuntimeException("Updating the assoc def sequence failed", e);
544        }
545    }
546
547
548
549    // === Store (DB only) ===
550
551    private void storeDataTypeUri() {
552        // remove current assignment
553        getRelatedTopic("dmx.core.aggregation", "dmx.core.type", "dmx.core.default", "dmx.core.data_type")
554            .getRelatingAssociation().delete();
555        // create new assignment
556        pl.typeStorage.storeDataType(uri, dataTypeUri);
557    }
558
559    private void indexAllInstances(IndexMode indexMode) {
560        List<? extends DMXObjectModelImpl> objects = getAllInstances();
561        //
562        String str = "\"" + value + "\" (" + uri + ") instances";
563        if (indexModes.size() > 0) {
564            if (objects.size() > 0) {
565                logger.info("### Indexing " + objects.size() + " " + str + " (indexMode=" + indexMode + ")");
566            } else {
567                logger.info("### Indexing " + str + " SKIPPED -- no instances in DB");
568            }
569        } else {
570            logger.info("### Indexing " + str + " SKIPPED -- no index mode set");
571        }
572        //
573        for (DMXObjectModelImpl obj : objects) {
574            obj.indexSimpleValue(indexMode);
575        }
576    }
577
578
579
580    // === Association Definitions (memory access) ===
581
582    /**
583     * Finds an assoc def by ID and returns its URI (at index 0). Returns the URI of the next-in-sequence
584     * assoc def as well (at index 1), or null if the found assoc def is the last one.
585     */
586    private String[] findAssocDefUris(long assocDefId) {
587        if (assocDefId == -1) {
588            throw new IllegalArgumentException("findAssocDefUris() called with assocDefId=-1");
589        }
590        String[] assocDefUris = new String[2];
591        Iterator<String> i = iterator();
592        while (i.hasNext()) {
593            String assocDefUri = i.next();
594            long _assocDefId = checkAssocDefId(_getAssocDef(assocDefUri));
595            if (_assocDefId == assocDefId) {
596                assocDefUris[0] = assocDefUri;
597                if (i.hasNext()) {
598                    assocDefUris[1] = i.next();
599                }
600                break;
601            }
602        }
603        if (assocDefUris[0] == null) {
604            throw new RuntimeException("Assoc def with ID " + assocDefId + " not found in assoc defs of type \"" + uri +
605                "\" (" + assocDefs.keySet() + ")");
606        }
607        return assocDefUris;
608    }
609
610    // ### TODO: not called
611    private boolean hasSameAssocDefSequence(Collection<? extends AssociationDefinitionModel> assocDefs) {
612        Collection<? extends AssociationDefinitionModel> _assocDefs = getAssocDefs();
613        if (assocDefs.size() != _assocDefs.size()) {
614            return false;
615        }
616        //
617        Iterator<? extends AssociationDefinitionModel> i = assocDefs.iterator();
618        for (AssociationDefinitionModel _assocDef : _assocDefs) {
619            AssociationDefinitionModel assocDef = i.next();
620            // Note: if the assoc def's custom association type changed the assoc def URI changes as well.
621            // So we must identify the assoc defs to compare **by ID**.
622            long assocDefId  = checkAssocDefId(assocDef);
623            long _assocDefId = checkAssocDefId(_assocDef);
624            if (assocDefId != _assocDefId) {
625                return false;
626            }
627        }
628        //
629        return true;
630    }
631
632    // ---
633
634    private void rehashAssocDefs(Collection<AssociationDefinitionModelImpl> newAssocDefs) {
635        for (AssociationDefinitionModel assocDef : newAssocDefs) {
636            rehashAssocDef(assocDef.getAssocDefUri(), null);
637        }
638    }
639
640    private void rehashAssocDef(String assocDefUri, String beforeAssocDefUri) {
641        AssociationDefinitionModel assocDef = removeAssocDef(assocDefUri);
642        logger.info("Rehashing assoc def \"" + assocDefUri + "\" -> \"" + assocDef.getAssocDefUri() + "\" (put " +
643            (beforeAssocDefUri != null ? "before \"" + beforeAssocDefUri + "\"" : "at end") + ")");
644        addAssocDefBefore(assocDef, beforeAssocDefUri);
645    }
646
647    // ---
648
649    private AssociationDefinitionModelImpl getAssocDefOrThrow(String assocDefUri) {
650        AssociationDefinitionModelImpl assocDef = _getAssocDef(assocDefUri);
651        if (assocDef == null) {
652            throw new RuntimeException("Assoc def \"" + assocDefUri + "\" not found in " + assocDefs.keySet());
653        }
654        return assocDef;
655    }
656
657    private AssociationDefinitionModelImpl _getAssocDef(String assocDefUri) {
658        return assocDefs.get(assocDefUri);
659    }
660
661    // ---
662
663    /**
664     * Returns the ID of the last association definition of this type or
665     * <code>-1</code> if there are no association definitions.
666     */
667    private long lastAssocDefId() {
668        long lastAssocDefId = -1;
669        for (AssociationDefinitionModel assocDef : getAssocDefs()) {
670            lastAssocDefId = assocDef.getId();
671        }
672        return lastAssocDefId;
673    }
674
675    private long firstAssocDefId() {
676        return getAssocDefs().iterator().next().getId();
677    }
678
679    private String getFirstAssocDefUri() {
680        Iterator<String> i = iterator();
681        return i.hasNext() ? i.next() : null;
682    }
683
684    // ---
685
686    private long checkAssocDefId(AssociationDefinitionModel assocDef) {
687        long assocDefId = assocDef.getId();
688        if (assocDefId == -1) {
689            throw new RuntimeException("The assoc def ID is uninitialized (-1): " + assocDef);
690        }
691        return assocDefId;
692    }
693
694    // ---
695
696    private SequencedHashMap<String, AssociationDefinitionModelImpl> toMap(
697                                                           Collection<? extends AssociationDefinitionModel> assocDefs) {
698        SequencedHashMap<String, AssociationDefinitionModelImpl> _assocDefs = new SequencedHashMap();
699        for (AssociationDefinitionModel assocDef : assocDefs) {
700            _assocDefs.put(assocDef.getAssocDefUri(), (AssociationDefinitionModelImpl) assocDef);
701        }
702        return _assocDefs;
703    }
704
705
706
707    // === Type Cache (memory access) ===
708
709    private void putInTypeCache() {
710        pl.typeStorage.putInTypeCache(this);
711    }
712
713    /**
714     * Removes this type from type cache and adds a DELETE TYPE directive to the given set of directives.
715     */
716    private void removeFromTypeCache() {
717        pl.typeStorage.removeFromTypeCache(uri);
718        //
719        Directive dir = getDeleteTypeDirective();   // abstract
720        Directives.get().add(dir, new JSONWrapper("uri", uri));
721    }
722
723
724
725    // === Serialization ===
726
727    private JSONArray toJSONArray(List<IndexMode> indexModes) {
728        JSONArray indexModeUris = new JSONArray();
729        for (IndexMode indexMode : indexModes) {
730            indexModeUris.put(indexMode.toUri());
731        }
732        return indexModeUris;
733    }
734
735    private JSONArray toJSONArray(Collection<? extends AssociationDefinitionModel> assocDefs) {
736        JSONArray _assocDefs = new JSONArray();
737        for (AssociationDefinitionModel assocDef : assocDefs) {
738            _assocDefs.put(assocDef.toJSON());
739        }
740        return _assocDefs;
741    }
742
743
744
745    // ------------------------------------------------------------------------------------------------- Private Classes
746
747    private static final class JSONWrapper implements JSONEnabled {
748
749        private JSONObject wrapped;
750
751        private JSONWrapper(String key, Object value) {
752            try {
753                wrapped = new JSONObject();
754                wrapped.put(key, value);
755            } catch (Exception e) {
756                throw new RuntimeException("Constructing a JSONWrapper failed", e);
757            }
758        }
759
760        @Override
761        public JSONObject toJSON() {
762            return wrapped;
763        }
764    }
765}