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 }