001: /*
002: * Copyright 2004-2006 the original author or authors.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.compass.core.mapping.osem;
018:
019: import java.lang.reflect.Constructor;
020: import java.util.ArrayList;
021: import java.util.HashMap;
022: import java.util.HashSet;
023: import java.util.Iterator;
024: import java.util.List;
025: import java.util.Set;
026:
027: import org.compass.core.engine.naming.PropertyPath;
028: import org.compass.core.mapping.AbstractResourceMapping;
029: import org.compass.core.mapping.AliasMapping;
030: import org.compass.core.mapping.Mapping;
031: import org.compass.core.mapping.MappingException;
032: import org.compass.core.mapping.PostProcessingMapping;
033: import org.compass.core.mapping.ResourceMapping;
034: import org.compass.core.mapping.ResourcePropertyMapping;
035: import org.compass.core.util.Assert;
036:
037: /**
038: * @author kimchy
039: */
040: public class ClassMapping extends AbstractResourceMapping implements
041: ResourceMapping, PostProcessingMapping {
042:
043: private ClassPropertyMapping.ManagedId managedId;
044:
045: private PropertyPath classPath;
046:
047: private Class clazz;
048:
049: private boolean poly;
050:
051: private Class polyClass;
052:
053: private Boolean supportUnmarshall;
054:
055: private ResourcePropertyMapping[] resourcePropertyMappings;
056:
057: private ClassPropertyMapping[] classPropertyMappings;
058:
059: private ClassIdPropertyMapping[] classIdPropertyMappings;
060:
061: private HashMap<String, ResourcePropertyMapping> pathMappings;
062:
063: private Constructor constructor;
064:
065: private Constructor polyConstructor;
066:
067: public Mapping copy() {
068: ClassMapping copy = new ClassMapping();
069: super .copy(copy);
070: copy.setPoly(isPoly());
071: copy.setClassPath(getClassPath());
072: copy.setClazz(getClazz());
073: copy.setPolyClass(getPolyClass());
074: copy.setConstructor(getConstructor());
075: copy.setPolyConstructor(getPolyConstructor());
076: copy.supportUnmarshall = supportUnmarshall;
077: copy.setManagedId(getManagedId());
078: return copy;
079: }
080:
081: public AliasMapping shallowCopy() {
082: ClassMapping copy = new ClassMapping();
083: super .shallowCopy(copy);
084: copy.setPoly(isPoly());
085: copy.setClassPath(getClassPath());
086: copy.setClazz(getClazz());
087: copy.setPolyClass(getPolyClass());
088: copy.setConstructor(getConstructor());
089: copy.setPolyConstructor(getPolyConstructor());
090: copy.supportUnmarshall = supportUnmarshall;
091: copy.setManagedId(getManagedId());
092: return copy;
093: }
094:
095: /**
096: * Post process by using the dynamic find operations to cache them.
097: *
098: * @throws MappingException
099: */
100: protected void doPostProcess() throws MappingException {
101: PostProcessMappingCallback callback = new PostProcessMappingCallback();
102: OsemMappingIterator.iterateMappings(callback, this , true);
103: resourcePropertyMappings = callback
104: .getResourcePropertyMappings().toArray(
105: new ResourcePropertyMapping[callback
106: .getResourcePropertyMappings().size()]);
107: classPropertyMappings = callback.getClassPropertyMappings()
108: .toArray(
109: new ClassPropertyMapping[callback
110: .getClassPropertyMappings().size()]);
111: List<ClassIdPropertyMapping> idMappings = findClassPropertyIdMappings();
112: classIdPropertyMappings = idMappings
113: .toArray(new ClassIdPropertyMapping[idMappings.size()]);
114: pathMappings = callback.getPathMappings();
115: }
116:
117: public ResourcePropertyMapping[] getResourcePropertyMappings() {
118: return resourcePropertyMappings;
119: }
120:
121: public ClassPropertyMapping[] getClassPropertyMappings() {
122: return classPropertyMappings;
123: }
124:
125: public ClassIdPropertyMapping[] getClassIdPropertyMappings() {
126: return classIdPropertyMappings;
127: }
128:
129: /**
130: * Dynamically find the id mappings.
131: */
132: public List<Mapping> findIdMappings() {
133: ArrayList<Mapping> idMappingList = new ArrayList<Mapping>();
134: for (Iterator it = mappingsIt(); it.hasNext();) {
135: Mapping m = (Mapping) it.next();
136: if (m instanceof ClassIdPropertyMapping) {
137: idMappingList.add(m);
138: }
139: if (m instanceof IdComponentMapping) {
140: idMappingList.add(m);
141: }
142: }
143: return idMappingList;
144: }
145:
146: /**
147: * Dynamically finds all the {@link ClassIdPropertyMapping}s for the class.
148: */
149: public List<ClassIdPropertyMapping> findClassPropertyIdMappings() {
150: ArrayList<ClassIdPropertyMapping> idMappingList = new ArrayList<ClassIdPropertyMapping>();
151: for (Iterator it = mappingsIt(); it.hasNext();) {
152: Mapping m = (Mapping) it.next();
153: if (m instanceof ClassIdPropertyMapping) {
154: idMappingList.add((ClassIdPropertyMapping) m);
155: }
156: if (m instanceof IdComponentMapping) {
157: IdComponentMapping idComponentMapping = (IdComponentMapping) m;
158: idMappingList.addAll(idComponentMapping
159: .getRefClassMappings()[0]
160: .findClassPropertyIdMappings());
161: }
162: }
163: return idMappingList;
164: }
165:
166: public ResourcePropertyMapping getResourcePropertyMappingByDotPath(
167: String path) {
168: return pathMappings.get(path);
169: }
170:
171: public ClassPropertyMapping.ManagedId getManagedId() {
172: return managedId;
173: }
174:
175: public void setManagedId(ClassPropertyMapping.ManagedId managedId) {
176: this .managedId = managedId;
177: }
178:
179: public boolean isPoly() {
180: return poly;
181: }
182:
183: public void setPoly(boolean poly) {
184: this .poly = poly;
185: }
186:
187: public PropertyPath getClassPath() {
188: return classPath;
189: }
190:
191: public void setClassPath(PropertyPath classPath) {
192: this .classPath = classPath;
193: }
194:
195: public Class getClazz() {
196: return clazz;
197: }
198:
199: public void setClazz(Class clazz) {
200: this .clazz = clazz;
201: }
202:
203: /**
204: * In case poly is set to <code>true</code>, this will be the class that will
205: * be instanciated for all persisted classes. If not set, Compass will persist
206: * the actual class in the index, and will use it to instanciate the class.
207: */
208: public Class getPolyClass() {
209: return polyClass;
210: }
211:
212: public void setPolyClass(Class polyClass) {
213: this .polyClass = polyClass;
214: }
215:
216: public boolean isSupportUnmarshall() {
217: // possible NPE, will take care of it in setting it
218: return supportUnmarshall;
219: }
220:
221: public void setSupportUnmarshall(boolean supportUnmarshall) {
222: this .supportUnmarshall = supportUnmarshall;
223: }
224:
225: public boolean isSupportUnmarshallSet() {
226: return supportUnmarshall != null;
227: }
228:
229: public Constructor getConstructor() {
230: return constructor;
231: }
232:
233: public void setConstructor(Constructor constructor) {
234: this .constructor = constructor;
235: }
236:
237: public Constructor getPolyConstructor() {
238: return polyConstructor;
239: }
240:
241: public void setPolyConstructor(Constructor polyConstructor) {
242: this .polyConstructor = polyConstructor;
243: }
244:
245: public class PostProcessMappingCallback
246: extends
247: OsemMappingIterator.ClassPropertyAndResourcePropertyGatherer {
248:
249: private HashMap<String, ResourcePropertyMapping> pathMappings = new HashMap<String, ResourcePropertyMapping>();
250:
251: private ArrayList<String> pathSteps = new ArrayList<String>();
252:
253: private StringBuffer sb = new StringBuffer();
254:
255: private Set<NoUnmarshallHolder> cyclicNoUnmarshallRefAliasMappings = new HashSet<NoUnmarshallHolder>();
256:
257: private Set<String> cyclicClassMappings = new HashSet<String>();
258:
259: class NoUnmarshallHolder {
260: ClassMapping parent;
261: ClassMapping classMapping;
262:
263: NoUnmarshallHolder(ClassMapping parent,
264: ClassMapping classMapping) {
265: this .parent = parent;
266: this .classMapping = classMapping;
267: }
268: }
269:
270: /**
271: * In case we do not need to support unmarshalling, we need to perform simple cyclic detection
272: * and return <code>false</code> (won't iterate into this class mapping) if we already passed
273: * this class mapping. We will remove the marker in the {@link #onEndClassMapping(ClassMapping)}.
274: */
275: public boolean onBeginClassMapping(ClassMapping classMapping) {
276: if (classMapping.isSupportUnmarshall()) {
277: return true;
278: }
279: if (cyclicClassMappings.contains(classMapping.getAlias())) {
280: return false;
281: }
282: cyclicClassMappings.add(classMapping.getAlias());
283: return true;
284: }
285:
286: /**
287: * If we do not support unmarshalling, we need to clean up our marker for this class mapping.
288: */
289: public void onEndClassMapping(ClassMapping classMapping) {
290: if (classMapping.isSupportUnmarshall()) {
291: return;
292: }
293: cyclicClassMappings.remove(classMapping.getAlias());
294: }
295:
296: /**
297: * <p>Since we did not process duplicate mappings, we need to replace them with the original mappings that
298: * were processed (for example, we added intenral ids to it where needed).
299: */
300: protected void onDuplicateMapping(ClassMapping classMapping,
301: ObjectMapping actualMapping,
302: ObjectMapping duplicateMapping) {
303: Assert.isTrue(actualMapping.getPropertyName().equals(
304: duplicateMapping.getPropertyName()),
305: "Internal Error in Compass, Original["
306: + duplicateMapping.getName()
307: + "] does not equal ["
308: + actualMapping.getName() + "]");
309:
310: // TODO since we replace the mappings here, some attributes will be inacurate (like objClass) for the replaced class mapping
311: int index = classMapping.mappings.indexOf(duplicateMapping);
312: if (index < 0) {
313: // let's look in the collection, if we find it as an element
314: // then we just replace it (the duplicate mapping might raise
315: // a duplicate for a collection, but with the collection element)
316: for (int i = 0; i < classMapping.mappings.size(); i++) {
317: Object o = classMapping.mappings.get(i);
318: if (o instanceof AbstractCollectionMapping) {
319: AbstractCollectionMapping temp = (AbstractCollectionMapping) o;
320: if (temp.getElementMapping() == duplicateMapping) {
321: temp.setElementMapping(actualMapping);
322: index = i;
323: break;
324: }
325: }
326: }
327: } else {
328: classMapping.mappingsByNameMap.put(duplicateMapping
329: .getName(), actualMapping);
330: classMapping.mappings.set(index, actualMapping);
331: }
332: if (index < 0) {
333: throw new IllegalStateException(
334: "Internal Error in Compass, original mapping ["
335: + duplicateMapping.getName()
336: + "] not found");
337: }
338: }
339:
340: private void addToPath(Mapping mapping) {
341: pathSteps.add(mapping.getName());
342: }
343:
344: private void removeFromPath(Mapping mapping) {
345: if (pathSteps.size() > 0) {
346: pathSteps.remove(pathSteps.size() - 1);
347: }
348: }
349:
350: /**
351: */
352: public boolean onBeginMultipleMapping(
353: ClassMapping classMapping, Mapping mapping) {
354: boolean retVal = super .onBeginMultipleMapping(classMapping,
355: mapping);
356: addToPath(mapping);
357: return retVal;
358: }
359:
360: public void onEndMultiplMapping(ClassMapping classMapping,
361: Mapping mapping) {
362: super .onEndMultiplMapping(classMapping, mapping);
363: removeFromPath(mapping);
364: }
365:
366: public void onClassPropertyMapping(ClassMapping classMapping,
367: ClassPropertyMapping mapping) {
368: super .onClassPropertyMapping(classMapping, mapping);
369: ResourcePropertyMapping resourcePropertyMapping = mapping
370: .getIdMapping();
371: pathMappings.put(currentPath(), resourcePropertyMapping);
372: }
373:
374: public void onResourcePropertyMapping(
375: ResourcePropertyMapping mapping) {
376: super .onResourcePropertyMapping(mapping);
377: if (!mapping.isInternal()) {
378: addToPath(mapping);
379: }
380: pathMappings.put(currentPath(), mapping);
381: if (!mapping.isInternal()) {
382: removeFromPath(mapping);
383: }
384: }
385:
386: public HashMap<String, ResourcePropertyMapping> getPathMappings() {
387: return pathMappings;
388: }
389:
390: private String currentPath() {
391: sb.setLength(0);
392: for (int i = 0; i < pathSteps.size(); i++) {
393: if (i > 0) {
394: sb.append('.');
395: }
396: sb.append(pathSteps.get(i));
397: }
398: return sb.toString();
399: }
400: }
401:
402: }
|