001    package org.cumulus4j.store.model;
002    
003    import java.util.ArrayList;
004    import java.util.Collection;
005    import java.util.HashMap;
006    import java.util.Map;
007    import java.util.concurrent.atomic.AtomicLong;
008    
009    import javax.jdo.annotations.Column;
010    import javax.jdo.annotations.Discriminator;
011    import javax.jdo.annotations.DiscriminatorStrategy;
012    import javax.jdo.annotations.IdentityType;
013    import javax.jdo.annotations.Inheritance;
014    import javax.jdo.annotations.InheritanceStrategy;
015    import javax.jdo.annotations.NotPersistent;
016    import javax.jdo.annotations.PersistenceCapable;
017    import javax.jdo.annotations.Queries;
018    import javax.jdo.annotations.Query;
019    import javax.jdo.annotations.Unique;
020    import javax.jdo.annotations.Uniques;
021    
022    import org.datanucleus.ExecutionContext;
023    
024    @PersistenceCapable(identityType=IdentityType.APPLICATION, detachable="true")
025    @Inheritance(strategy=InheritanceStrategy.NEW_TABLE)
026    @Discriminator(strategy=DiscriminatorStrategy.VALUE_MAP, value="EmbeddedClassMeta")
027    @Uniques({
028            @Unique(members={"embeddingFieldMeta_fieldID"}, columns=@Column(name="discriminator"))
029    })
030    @Queries({
031            @Query(
032                            name=EmbeddedClassMeta.NamedQueries.getEmbeddedClassMetaByEmbeddingFieldMeta_fieldID,
033                            value="SELECT UNIQUE FROM org.cumulus4j.store.model.EmbeddedClassMeta EXCLUDE SUBCLASSES WHERE this.embeddingFieldMeta_fieldID == :embeddingFieldMeta_fieldID"
034            )
035    })
036    public class EmbeddedClassMeta extends ClassMeta {
037    
038            protected static final String UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META = EmbeddedClassMeta.class.getSimpleName() + '.';
039    
040            protected static class NamedQueries {
041                    public static final String getEmbeddedClassMetaByEmbeddingFieldMeta_fieldID = "getEmbeddedClassMetaByEmbeddingFieldMeta_fieldID";
042            }
043    
044    //      @Persistent(nullValue=NullValue.EXCEPTION)
045            @NotPersistent
046            private ClassMeta nonEmbeddedClassMeta;
047    
048            private long nonEmbeddedClassMeta_classID;
049    
050    //      @Persistent(nullValue=NullValue.EXCEPTION)
051            @NotPersistent
052            private FieldMeta embeddingFieldMeta;
053    
054            private long embeddingFieldMeta_fieldID;
055    
056            @NotPersistent
057            private Map<FieldMeta, EmbeddedFieldMeta> nonEmbeddedFieldMeta2EmbeddedFieldMeta;
058    
059            protected EmbeddedClassMeta() { }
060    
061            public EmbeddedClassMeta(ExecutionContext executionContext, ClassMeta nonEmbeddedClassMeta, FieldMeta embeddingFieldMeta) {
062                    super(executionContext.getClassLoaderResolver().classForName(nonEmbeddedClassMeta.getClassName()));
063                    if (embeddingFieldMeta == null)
064                            throw new IllegalArgumentException("embeddingFieldMeta == null");
065    
066                    if (nonEmbeddedClassMeta instanceof EmbeddedClassMeta)
067    //                      nonEmbeddedClassMeta = ((EmbeddedClassMeta) nonEmbeddedClassMeta).getNonEmbeddedClassMeta();
068                            throw new IllegalArgumentException("nonEmbeddedClassMeta is an instance of EmbeddedClassMeta: " + nonEmbeddedClassMeta);
069    
070                    this.nonEmbeddedClassMeta = nonEmbeddedClassMeta;
071                    this.nonEmbeddedClassMeta_classID = nonEmbeddedClassMeta.getClassID();
072                    if (nonEmbeddedClassMeta_classID < 0)
073                            throw new IllegalStateException("nonEmbeddedClassMeta not yet persisted: " + nonEmbeddedClassMeta);
074    
075                    this.embeddingFieldMeta = embeddingFieldMeta;
076                    this.embeddingFieldMeta_fieldID = embeddingFieldMeta.getFieldID();
077                    if (embeddingFieldMeta_fieldID < 0)
078                            throw new IllegalStateException("embeddingFieldMeta not yet persisted: " + embeddingFieldMeta);
079    
080    //              setUniqueScope(null); // set in jdoPreStore, because id not assigned, yet
081                    setUniqueScope(UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META + embeddingFieldMeta_fieldID);
082            }
083    
084            @Override
085            public void addFieldMeta(FieldMeta fieldMeta) {
086                    if (!(fieldMeta instanceof EmbeddedFieldMeta)) {
087                            throw new IllegalArgumentException("fieldMeta is NOT an instance of EmbeddedFieldMeta: " + fieldMeta);
088                    }
089                    super.addFieldMeta(fieldMeta);
090                    Map<FieldMeta, EmbeddedFieldMeta> nefm2efmMap = nonEmbeddedFieldMeta2EmbeddedFieldMeta;
091                    if (nefm2efmMap != null) {
092                            EmbeddedFieldMeta embeddedFieldMeta = (EmbeddedFieldMeta) fieldMeta;
093                            nefm2efmMap.put(embeddedFieldMeta.getNonEmbeddedFieldMeta(), embeddedFieldMeta);
094                    }
095            }
096    
097            @Override
098            public void removeFieldMeta(FieldMeta fieldMeta) {
099                    super.removeFieldMeta(fieldMeta);
100                    nonEmbeddedFieldMeta2EmbeddedFieldMeta = null;
101            }
102    
103            /**
104             * Get the non-embedded {@link ClassMeta} of which this instance is a reference wihtin the scope of
105             * the {@link #getEmbeddingFieldMeta()}.
106             * @return the non-embedded {@link ClassMeta} (the one representing FCOs). Never <code>null</code>.
107             */
108            public ClassMeta getNonEmbeddedClassMeta() {
109                    if (nonEmbeddedClassMeta == null) {
110                            nonEmbeddedClassMeta = new ClassMetaDAO(getPersistenceManager()).getClassMeta(nonEmbeddedClassMeta_classID, true);
111                    }
112                    return nonEmbeddedClassMeta;
113            }
114    
115            /**
116             * Get the field embedding this pseudo-class.
117             * <p>
118             * This may be an {@link EmbeddedFieldMeta}, if this is a nested-embedded-field-situation.
119             * @return the field embedding this pseudo-class. Never <code>null</code>.
120             */
121            public FieldMeta getEmbeddingFieldMeta() {
122                    if (embeddingFieldMeta == null) {
123                            embeddingFieldMeta = new FieldMetaDAO(getPersistenceManager()).getFieldMeta(embeddingFieldMeta_fieldID, true);
124                    }
125                    return embeddingFieldMeta;
126            }
127    
128    //      @Override
129    //      public void jdoPreStore() {
130    //              super.jdoPreStore();
131    //              if (getUniqueScope() == null || !getUniqueScope().startsWith(UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META)) {
132    //                      setUniqueScope("TEMPORARY_" + UUID.randomUUID());
133    //                      if (nonEmbeddedClassMeta_classID < 0)
134    //                              nonEmbeddedClassMeta_classID = nextTemp_nonEmbeddedClassMeta_classID.decrementAndGet();
135    //
136    //                      if (embeddingFieldMeta_fieldID < 0)
137    //                              embeddingFieldMeta_fieldID = nextTemp_embeddingFieldMeta_fieldID.decrementAndGet();
138    //
139    //                      PostStoreRunnableManager.getInstance().addRunnable(new Runnable() {
140    //                              @Override
141    //                              public void run() {
142    //                                      PersistenceManager pm = JDOHelper.getPersistenceManager(EmbeddedClassMeta.this);
143    //
144    //                                      if (nonEmbeddedClassMeta != null)
145    //                                              nonEmbeddedClassMeta = pm.makePersistent(nonEmbeddedClassMeta);
146    //
147    //                                      if (nonEmbeddedClassMeta_classID < 0 && nonEmbeddedClassMeta != null)
148    //                                              nonEmbeddedClassMeta_classID = nonEmbeddedClassMeta.getClassID();
149    //
150    //                                      if (nonEmbeddedClassMeta_classID < 0)
151    //                                              throw new IllegalStateException("nonEmbeddedClassMeta_classID < 0");
152    //
153    //                                      if (embeddingFieldMeta != null)
154    //                                              embeddingFieldMeta = pm.makePersistent(embeddingFieldMeta);
155    //
156    //                                      if (embeddingFieldMeta_fieldID < 0 && embeddingFieldMeta != null)
157    //                                              embeddingFieldMeta_fieldID = embeddingFieldMeta.getFieldID();
158    //
159    //                                      if (embeddingFieldMeta_fieldID < 0)
160    //                                              throw new IllegalStateException("embeddingFieldMeta_fieldID < 0");
161    //
162    //                                      setUniqueScope(UNIQUE_SCOPE_PREFIX_EMBEDDED_CLASS_META + embeddingFieldMeta.getFieldID());
163    //
164    //                                      pm.flush();
165    //                              }
166    //                      });
167    //              }
168    //      }
169    
170            private static AtomicLong nextTemp_nonEmbeddedClassMeta_classID = new AtomicLong();
171            private static AtomicLong nextTemp_embeddingFieldMeta_fieldID = new AtomicLong();
172    
173            /**
174             * Get the {@link FieldMeta} managed by this instances corresponding to the given <code>fieldMeta</code>.
175             * <p>
176             * The given <code>fieldMeta</code> can be a sub-FieldMeta (not directly assigned to the corresponding ClassMeta,
177             * but assigned to one of its FieldMetas).
178             * @param fieldMeta a non-embedded {@link FieldMeta} (i.e. <b>not</b> an instance of {@link EmbeddedFieldMeta}).
179             * @return
180             */
181            public EmbeddedFieldMeta getEmbeddedFieldMetaForNonEmbeddedFieldMeta(FieldMeta fieldMeta) {
182                    if (fieldMeta == null)
183                            throw new IllegalArgumentException("fieldMeta == null");
184    
185                    if (fieldMeta instanceof EmbeddedFieldMeta)
186                            throw new IllegalArgumentException("fieldMeta is an instance of EmbeddedFieldMeta, but it should be a non-embedded FieldMeta!");
187    
188                    if (!this.getNonEmbeddedClassMeta().equals(fieldMeta.getClassMeta()))
189                            throw new IllegalArgumentException("fieldMeta.classMeta != this.nonEmbeddedClassMeta");
190    
191                    if (nonEmbeddedFieldMeta2EmbeddedFieldMeta == null) {
192                            Map<FieldMeta, EmbeddedFieldMeta> m = new HashMap<FieldMeta, EmbeddedFieldMeta>();
193                            for (FieldMeta efm : getFieldMetasWithSubFieldMetas()) {
194                                    EmbeddedFieldMeta embeddedFieldMeta = (EmbeddedFieldMeta) efm;
195                                    m.put(embeddedFieldMeta.getNonEmbeddedFieldMeta(), embeddedFieldMeta);
196                            }
197                            nonEmbeddedFieldMeta2EmbeddedFieldMeta = m;
198                    }
199                    return nonEmbeddedFieldMeta2EmbeddedFieldMeta.get(fieldMeta);
200            }
201    
202            protected Collection<FieldMeta> getFieldMetasWithSubFieldMetas() {
203                    Collection<FieldMeta> result = new ArrayList<FieldMeta>();
204                    for (FieldMeta fieldMeta : getFieldMetas()) {
205                            populateFieldMetasWithSubFieldMetas(result, fieldMeta);
206                    }
207                    return result;
208            }
209            protected void populateFieldMetasWithSubFieldMetas(Collection<FieldMeta> result, FieldMeta fieldMeta) {
210                    result.add(fieldMeta);
211                    for (FieldMeta subFieldMeta : fieldMeta.getSubFieldMetas()) {
212                            populateFieldMetasWithSubFieldMetas(result, subFieldMeta);
213                    }
214            }
215    
216            @Override
217            public void jdoPostDetach(Object o) {
218                    final PostDetachRunnableManager postDetachRunnableManager = PostDetachRunnableManager.getInstance();
219                    postDetachRunnableManager.enterScope();
220                    try {
221                            super.jdoPostDetach(o);
222                            postDetachRunnableManager.addRunnable(new Runnable() {
223                                    @Override
224                                    public void run() {
225                                            DetachedClassMetaModel detachedClassMetaModel = DetachedClassMetaModel.getInstance();
226    
227                                            if (detachedClassMetaModel == null) // we currently only detach with this being present - at least it should, hence we don't need to handle things differently.
228                                                    throw new IllegalStateException("DetachedClassMetaModel.getInstance() returned null!");
229    
230                                            if (detachedClassMetaModel != null) {
231                                                    nonEmbeddedClassMeta = detachedClassMetaModel.getClassMeta(nonEmbeddedClassMeta_classID, false);
232                                                    if (nonEmbeddedClassMeta == null)
233                                                            setNonEmbeddedClassMetaPostponedInPostDetach(postDetachRunnableManager, detachedClassMetaModel, 1);
234                                            }
235                                    }
236                            });
237                    } finally {
238                            postDetachRunnableManager.exitScope();
239                    }
240            }
241    
242            protected void setNonEmbeddedClassMetaPostponedInPostDetach(final PostDetachRunnableManager postDetachRunnableManager, final DetachedClassMetaModel detachedClassMetaModel, final int postponeCounter) {
243                    postDetachRunnableManager.addRunnable(new Runnable() {
244                            @Override
245                            public void run() {
246                                    nonEmbeddedClassMeta = detachedClassMetaModel.getClassMeta(nonEmbeddedClassMeta_classID, false);
247                                    if (nonEmbeddedClassMeta == null) {
248                                            final int maxPostponeCounter = 100;
249                                            if (postponeCounter > maxPostponeCounter)
250                                                    throw new IllegalStateException("postponeCounter > " + maxPostponeCounter);
251    
252                                            setNonEmbeddedClassMetaPostponedInPostDetach(postDetachRunnableManager, detachedClassMetaModel, postponeCounter + 1);
253                                    }
254                            }
255                    });
256            }
257    }