001: /*
002: * Copyright 2001-2006 C:1 Financial Services GmbH
003: *
004: * This software is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU Lesser General Public
006: * License Version 2.1, as published by the Free Software Foundation.
007: *
008: * This software is distributed in the hope that it will be useful,
009: * but WITHOUT ANY WARRANTY; without even the implied warranty of
010: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
011: * Lesser General Public License for more details.
012: *
013: * You should have received a copy of the GNU Lesser General Public
014: * License along with this library; if not, write to the Free Software
015: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA
016: */
017:
018: package de.finix.contelligent.core;
019:
020: import java.util.Collections;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.Map;
024:
025: import de.finix.contelligent.Component;
026: import de.finix.contelligent.ComponentPath;
027: import de.finix.contelligent.Type;
028: import de.finix.contelligent.exception.ContelligentExceptionID;
029: import de.finix.contelligent.exception.TypeException;
030: import de.finix.contelligent.logging.LoggingService;
031: import de.finix.contelligent.persistence.TypeProperty;
032: import de.finix.contelligent.xml.elements.TypeMetainfoElement;
033:
034: /**
035: * Implementation of interface {@link Type}.
036: */
037: final public class TypeImpl implements Type {
038: final static org.apache.log4j.Logger log = LoggingService
039: .getLogger(TypeImpl.class);
040:
041: private long id;
042:
043: private String name;
044:
045: final private int hashCode;
046:
047: final private String typeGroup;
048:
049: final private String super TypeName;
050:
051: private String className = null;
052:
053: final private Map originalPropertyMap;
054:
055: private Class typeClass;
056:
057: private TypeMetainfoElement metaInfo = null;
058:
059: private Type super Type;
060:
061: private ComponentPath blueprintPath;
062:
063: /**
064: * Contains the properties of this type including those inherited from its
065: * super-type(s). The map contains (String/TypeProperty) entries mapping the
066: * name of a property to the corresponding {@link TypeProperty} instance.
067: */
068: private Map mergedPropertyMap = null;
069:
070: /**
071: * Is true if this type doesn't extend another type but only restricts it
072: * which means it does only overwrite some properties but uses the same
073: * class as th super type.
074: */
075: final private boolean restrictsOnly;
076:
077: public TypeImpl(long id, String name, String typeGroup,
078: String super TypeName, TypeMetainfoElement metaInfo,
079: Map originalPropertyMap, Map mergedPropertyMap,
080: String className, boolean restrictsOnly,
081: ComponentPath blueprintPath) throws TypeException {
082:
083: if (name == null) {
084: throw new TypeException();
085: }
086:
087: if (originalPropertyMap == null) {
088: throw new TypeException();
089: }
090:
091: this .id = id;
092: this .name = name;
093: this .typeGroup = typeGroup;
094: this .super TypeName = super TypeName;
095: this .originalPropertyMap = originalPropertyMap;
096: this .mergedPropertyMap = mergedPropertyMap; // maybe null
097: this .hashCode = name.hashCode();
098: this .metaInfo = metaInfo;
099: this .className = className;
100: this .restrictsOnly = restrictsOnly;
101: this .blueprintPath = blueprintPath;
102: }
103:
104: /**
105: * Creates a new <code>Type</code> instance without setting the
106: * {@link #setMergedPropertyMap merged property-map} yet.
107: *
108: * @param name
109: * a <code>String</code> value
110: * @param typeGroup
111: * a <code>String</code> value
112: * @param superTypeName
113: * a <code>String</code> value
114: * @param originalPropertyMap
115: * a <code>Map</code> value
116: * @exception TypeException
117: * if an error occurs
118: */
119: public TypeImpl(long id, String name, String typeGroup,
120: String super TypeName, TypeMetainfoElement metaInfo,
121: Map originalPropertyMap, String className,
122: boolean restrictsOnly) throws TypeException {
123: this (id, name, typeGroup, super TypeName, metaInfo,
124: originalPropertyMap, null, className, restrictsOnly,
125: null);
126: }
127:
128: public boolean isBlueprint() {
129: return (blueprintPath != null);
130: }
131:
132: public ComponentPath getBlueprintPath() {
133: return blueprintPath;
134: }
135:
136: public void setBlueprintPath(ComponentPath blueprintPath) {
137: this .blueprintPath = blueprintPath;
138: }
139:
140: public long getId() {
141: return id;
142: }
143:
144: public void setId(long id) {
145: this .id = id;
146: }
147:
148: public String getName() {
149: return name;
150: }
151:
152: public void setName(String name) {
153: this .name = name;
154: }
155:
156: public TypeMetainfoElement getMetainfoElement() {
157: return metaInfo;
158: }
159:
160: public String getTypeGroup() {
161: return typeGroup;
162: }
163:
164: void setClassName(String className) {
165: this .className = className;
166: }
167:
168: public String getClassName() {
169: return this .className;
170: }
171:
172: void setTypeClass(Class typeClass) {
173: this .typeClass = typeClass;
174: Iterator iterator = originalPropertyMap.values().iterator();
175: while (iterator.hasNext()) {
176: TypeProperty prop = (TypeProperty) iterator.next();
177: prop.setTypeClass(typeClass);
178: }
179: }
180:
181: public Class getTypeClass() {
182: return typeClass;
183: }
184:
185: public String getSuperTypeName() {
186: return super TypeName;
187: }
188:
189: public boolean restrictsOnly() {
190: return restrictsOnly;
191: }
192:
193: public Map getMergedPropertyMap() {
194: return mergedPropertyMap;
195: }
196:
197: void setMergedPropertyMap(Map propertyMap) {
198: mergedPropertyMap = propertyMap;
199: }
200:
201: public Map getOriginalPropertyMap() {
202: return originalPropertyMap;
203: }
204:
205: public String toString() {
206: return ("[Type name=" + name + ", class= " + className
207: + ", super=" + super TypeName + "]");
208: }
209:
210: /**
211: * Returns the hashcode of this Type. (If two objects are equal they should
212: * have the same hashCode.)
213: *
214: * @return a hashcode for this Type
215: */
216: public int hashCode() {
217: return hashCode;
218: }
219:
220: public boolean equals(Object anObject) {
221: if (this == anObject) {
222: return true;
223: }
224: if (anObject instanceof TypeImpl) {
225: TypeImpl anotherType = (TypeImpl) anObject;
226: return (this .name.equals(anotherType.name));
227: }
228: return false;
229: }
230:
231: /**
232: * Implementation of {@link Type#setProperties}.
233: */
234: public void setProperties(Component component, Map propertyValues)
235: throws TypeException {
236: Iterator iterator = getMergedPropertyMap().values().iterator();
237: if (propertyValues == null) {
238: propertyValues = Collections.EMPTY_MAP;
239: }
240: while (iterator.hasNext()) {
241: TypeProperty property = (TypeProperty) iterator.next();
242: String propertyName = property.getName();
243: Object propertyValue = property.getValue();
244: if (property.getMode().equals(TypeProperty.READONLY_NOSET)) {
245: // means property is read-only and can not be set
246: if (log.isDebugEnabled()) {
247: log
248: .debug("setProperties() - property '"
249: + property
250: + "' has mode 'READONLY_NOSET' => no set method called.");
251: }
252: } else {
253: if (propertyValues.containsKey(propertyName)) {
254: if (!property.isFinal()) {
255: propertyValue = propertyValues
256: .get(propertyName);
257: } else {
258: log
259: .warn("setProperties() - component '"
260: + component
261: + "' defines a value for property '"
262: + property
263: + "' although it's final! (value ignored)");
264: }
265: } else if (property.isRequired()) {
266: log.error("setProperties() - component '"
267: + component
268: + "' does not define required property '"
269: + property + "!");
270: throw new TypeException(
271: ContelligentExceptionID.type_requiredPropertyMissing,
272: new Object[] {
273: component.getComponentContext()
274: .getPath().toPath(),
275: property.getName() });
276: }
277: property.setProperty(component, propertyValue);
278: }
279: }
280: }
281:
282: public Map getProperties(Component component, boolean includeRN)
283: throws TypeException {
284: return getProperties(component, includeRN, true);
285: }
286:
287: /**
288: * Implementation of {@link Type#getProperties(Component,boolean,boolean)}.
289: */
290: public Map getProperties(Component component, boolean includeRN,
291: boolean includeW) throws TypeException {
292: final Map componentProps = new HashMap();
293: Iterator typeProperties = getMergedPropertyMap().values()
294: .iterator();
295:
296: while (typeProperties.hasNext()) {
297: TypeProperty property = (TypeProperty) typeProperties
298: .next();
299:
300: if (log.isDebugEnabled()) {
301: log.debug("getProperties() - processing property '"
302: + property + "' ...");
303: }
304:
305: if (property.isFinal()) {
306: continue;
307: }
308:
309: if (!includeW) {
310: if (property.getMode().equals(TypeProperty.WRITEONLY)) {
311: // means property is writeonly and can not be read
312: if (log.isDebugEnabled()) {
313: log
314: .debug("getProperties() - property '"
315: + property
316: + "' has mode 'WRITEONLY' => no read method called.");
317: }
318: continue;
319: }
320: }
321:
322: if (property.getMode()
323: .equals(TypeProperty.WRITEONLY_NOREAD)) {
324: // means property is writeType-only and can not be read
325: if (log.isDebugEnabled()) {
326: log
327: .debug("getProperties() - property '"
328: + property
329: + "' has mode 'WRITEONLY_NOREAD' => no read method called.");
330: }
331: continue;
332: }
333:
334: if ((!includeRN)
335: && property.getMode().equals(
336: TypeProperty.READONLY_NOSET)) {
337: // means property is writeType-only and can not be read
338: if (log.isDebugEnabled()) {
339: log
340: .debug("getProperties() - property '"
341: + property
342: + "' has mode 'READONLY_NOSET' => not addedt to property map.");
343: }
344: continue;
345: }
346:
347: Object propertyValue = property.getValue();
348: Object valueObject = property.getProperty(component);
349:
350: // add only to component property-map if instanceValue is not null
351: // and its
352: // value differs from that of the type-property:
353: if (valueObject != null
354: && !(propertyValue.equals(valueObject))) {
355: componentProps.put(property.getName(), valueObject);
356:
357: if (log.isDebugEnabled()) {
358: log
359: .debug("getProperties() - added property '"
360: + property.getName()
361: + "' with value '"
362: + valueObject
363: + "' to component property-map. (type-property has value '"
364: + propertyValue + "')");
365: }
366: }
367: } // while
368: if (log.isDebugEnabled()) {
369: log.debug("getProperties() - returning property-map: "
370: + componentProps);
371: }
372: return componentProps;
373: }
374:
375: /**
376: * Implementation of {@link Type#getProperties(Map, boolean)}.
377: */
378: public Map getProperties(Map componentPropertyMap, boolean includeRN)
379: throws TypeException {
380: final Map componentProps = new HashMap();
381: final boolean debugEnabled = log.isDebugEnabled();
382: TypeProperty typeProperty = null;
383: String valueString = null;
384: Map typePropertyMap = getMergedPropertyMap();
385: Iterator properties = componentPropertyMap.keySet().iterator();
386:
387: while (properties.hasNext()) {
388: String propertyName = (String) properties.next();
389: typeProperty = (TypeProperty) typePropertyMap
390: .get(propertyName);
391:
392: if (typeProperty == null) {
393: log.error("getProperties() - found property '"
394: + propertyName
395: + "' in map which is not defined by type '"
396: + this + "' (skipped)!");
397: continue;
398: }
399:
400: if (typeProperty.isFinal()) {
401: log
402: .warn("getProperties() - found property '"
403: + propertyName
404: + "' in map which is defined as final by type '"
405: + this + "' (skipped)!");
406: continue;
407: }
408:
409: if (typeProperty.getMode().equals(
410: TypeProperty.WRITEONLY_NOREAD)) {
411:
412: // means property is writeType-only and can not be read (and no
413: // persistence is possible!)
414: log.warn("getProperties() - found property '"
415: + propertyName + "' in map which has mode '"
416: + TypeProperty.WRITEONLY_NOREAD
417: + "' and hence can not be stored (skipped).");
418:
419: continue;
420: }
421:
422: if ((!includeRN)
423: && typeProperty.getMode().equals(
424: TypeProperty.READONLY_NOSET)) {
425: // means property is writeType-only and can not be read (and no
426: // persistence is possible!)
427: log.warn("getProperties() - found property '"
428: + propertyName + "' in map which has mode '"
429: + TypeProperty.READONLY_NOSET
430: + "' and hence is not stored (skipped).");
431: continue;
432: }
433:
434: valueString = (String) componentPropertyMap
435: .get(propertyName);
436: Object instanceValue = typeProperty
437: .convertProperty(valueString);
438: if (debugEnabled) {
439: log.debug("getProperties() - ... found value '"
440: + valueString + "' for property '"
441: + typeProperty + "' in map ...");
442: }
443:
444: // add only to component property-map if instance-value differs from
445: // type's one:
446: if (instanceValue != null
447: && (!typeProperty.getValue().equals(instanceValue))) {
448: componentProps.put(propertyName, instanceValue);
449: if (debugEnabled) {
450: log
451: .debug("getProperties() - added property '"
452: + propertyName
453: + "' with value '"
454: + instanceValue
455: + "' to component property-map. (type-property has value '"
456: + typeProperty.getValue() + "')");
457: }
458: }
459: }
460: if (debugEnabled)
461: log.debug("getProperties() - returning property-map: "
462: + componentProps);
463: return componentProps;
464: }
465:
466: public Type getSuperType() {
467: return super Type;
468: }
469:
470: void setSuperType(Type super Type) {
471: this .super Type = super Type;
472: }
473:
474: public boolean isSubTypeOf(String typeName) {
475: if (super Type == null) {
476: return false;
477: }
478: if (super Type.getName().equals(typeName)) {
479: return true;
480: }
481: return super Type.isSubTypeOf(typeName);
482: }
483:
484: public boolean isInstanceOf(String typeName) {
485: if (super Type == null) {
486: return false;
487: }
488: if (getName().equals(typeName)) {
489: return true;
490: }
491: return isSubTypeOf(typeName);
492: }
493:
494: }
|