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.client.base;
019:
020: import java.util.ArrayList;
021: import java.util.Collection;
022: import java.util.Collections;
023: import java.util.HashMap;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027: import java.util.StringTokenizer;
028: import java.util.Vector;
029: import java.util.logging.Level;
030: import java.util.logging.Logger;
031:
032: import org.w3c.dom.Element;
033: import org.w3c.dom.NodeList;
034:
035: import de.finix.contelligent.client.event.ContelligentEvent;
036: import de.finix.contelligent.client.event.ContelligentEventDispatcher;
037: import de.finix.contelligent.client.event.ContelligentEventListener;
038: import de.finix.contelligent.client.event.TaskFinishedEvent;
039: import de.finix.contelligent.client.event.TaskStartedEvent;
040: import de.finix.contelligent.client.event.TypeEvent;
041: import de.finix.contelligent.client.event.TypeEventListener;
042: import de.finix.contelligent.client.i18n.Resources;
043: import de.finix.contelligent.client.modules.preferences.PreferencesModule;
044: import de.finix.contelligent.client.remote.ActionResult;
045: import de.finix.contelligent.client.remote.Actions;
046: import de.finix.contelligent.client.remote.RemoteActionException;
047: import de.finix.contelligent.client.util.ExceptionDialog;
048: import de.finix.contelligent.client.util.xml.XMLUtil;
049:
050: /**
051: * Singleton factory to generate {@link Type} objects by name
052: */
053: public class TypeFactory extends ContelligentEventDispatcher {
054:
055: private static Logger logger = Logger.getLogger(TypeFactory.class
056: .getName());
057:
058: private static TypeFactory typeFactory = null;
059:
060: private Map<String, Type> types = new HashMap<String, Type>();
061:
062: private Map<String, Type> renameableTypes = new HashMap<String, Type>();
063:
064: private Map<String, Type> composedTypes = new HashMap<String, Type>();
065:
066: private final static Collection getTypeTokens(String types) {
067: Collection<String> typesCollection = new ArrayList<String>();
068: StringTokenizer tokenizer = new StringTokenizer(types, ",");
069: int tokens = tokenizer.countTokens();
070: for (int i = 0; i < tokens; i++) {
071: String type = tokenizer.nextToken().trim();
072: typesCollection.add(type);
073: }
074: return typesCollection;
075: }
076:
077: private final static boolean typeMatchesPattern(String type,
078: String pattern) {
079: if (pattern.endsWith("*")) {
080: return type.startsWith(pattern.substring(0, pattern
081: .length() - 1));
082: } else {
083: return type.equals(pattern);
084: }
085: }
086:
087: private final static boolean isAllowedType(String type,
088: Collection allowedTypes) {
089: for (Iterator it = allowedTypes.iterator(); it.hasNext();) {
090: String allowedType = (String) it.next();
091: if (typeMatchesPattern(type, allowedType))
092: return true;
093: }
094: return false;
095: }
096:
097: private TypeFactory() {
098: }
099:
100: public static TypeFactory getInstance() {
101: if (typeFactory == null) {
102: typeFactory = new TypeFactory();
103: }
104: return typeFactory;
105: }
106:
107: public Type getType(String name) throws TypeNotFoundException {
108: synchronized (types) {
109: if (!types.containsKey(name)) {
110: throw new TypeNotFoundException(Resources
111: .getLocalString("type_not_found",
112: new String[] { name }));
113: }
114: return (Type) types.get(name);
115: }
116: }
117:
118: /** In case you just need any type, this one gives you <em>any</em> type */
119: public Type getDummyType() {
120: synchronized (types) {
121: // in case there is a type loaded, just return first one as any one
122: // is as good as any other
123: if (types.size() > 0) {
124: Iterator i = types.values().iterator();
125: Type type = (Type) i.next();
126: return type;
127: } else {
128: return null;
129: }
130: }
131: }
132:
133: public Map<String, Type> getTypes() {
134: synchronized (types) {
135: return new HashMap<String, Type>(types);
136: }
137: }
138:
139: public Map<String, Type> getRenameableTypes() {
140: synchronized (renameableTypes) {
141: return new HashMap<String, Type>(renameableTypes);
142: }
143: }
144:
145: public Map<String, Type> getComposedTypes() {
146: synchronized (composedTypes) {
147: return new HashMap<String, Type>(composedTypes);
148: }
149: }
150:
151: public Map<String, Type> getAllowedTypes(String allowString) {
152: return getAllowedTypes(allowString, true);
153: }
154:
155: public Map<String, Type> getAllowedTypes(String allowString,
156: boolean emptyIsAll) {
157: Collection allowedTypePatterns = null;
158: if (allowString == null || allowString.length() == 0) {
159: if (!emptyIsAll) {
160: return Collections.EMPTY_MAP;
161: }
162: } else {
163: allowedTypePatterns = getTypeTokens(allowString);
164: }
165: Map<String, Type> allowedTypes = new HashMap<String, Type>();
166: synchronized (types) {
167: for (Iterator i = types.values().iterator(); i.hasNext();) {
168: Type type = (Type) i.next();
169: if (allowedTypePatterns == null
170: || isAllowedType(type.getName(),
171: allowedTypePatterns)) {
172: String typeGroup = type.getTypeGroup();
173: // Abstract types would cause an error when we try to create
174: // them
175: // The Link group is only used for core.Link which can also
176: // not be created the normal way.
177: if (!typeGroup.equals("abstract")
178: && !typeGroup.equals("link")
179: && !(typeGroup.equals("legacy") && !PreferencesModule
180: .getPreferences()
181: .getBoolean(
182: PreferencesModule.LEGACY_TYPES,
183: PreferencesModule.DEFAULT_LEGACY_TYPES))) {
184: allowedTypes.put(type.getName(), type);
185: }
186: }
187: }
188: }
189: return allowedTypes;
190: }
191:
192: public Map<String, Type> getTypes(PackageManager.Package pkg) {
193: Map<String, Type> packageTypes = new HashMap<String, Type>();
194: synchronized (types) {
195: for (Iterator i = types.values().iterator(); i.hasNext();) {
196: Type type = (Type) i.next();
197: if (type.getName().startsWith(pkg.getIdentifier())) {
198: packageTypes.put(type.getName(), type);
199: }
200: }
201: }
202: return packageTypes;
203: }
204:
205: // XXX should not be a DOM-parser, as data can get very large
206: // XXX HACK for sluggish reporting of loaded types
207: public void load() throws RemoteActionException {
208: TaskStartedEvent startedEvent = new TaskStartedEvent(this ,
209: Resources.getLocalString("loading_types"));
210: Session.getInstance().fireTaskStartEvent(startedEvent);
211:
212: boolean eventPending = false;
213: try {
214: Map<String, Type> loadedTypes = new HashMap<String, Type>();
215:
216: // get type definitions from server
217: ActionResult actionResult = Actions.getTypes();
218: Element typeDescription = actionResult.getContent();
219:
220: // parse result and generate types
221: NodeList typeList = typeDescription
222: .getElementsByTagName("type");
223: for (int i = 0; i < typeList.getLength(); i++) {
224: if (i % 50 == 0) {
225: if (eventPending) {
226: TaskFinishedEvent fe = new TaskFinishedEvent(
227: this );
228: Session.getInstance().fireTaskFinishEvent(fe);
229: eventPending = false;
230: }
231: TaskStartedEvent se = new TaskStartedEvent(this ,
232: Resources.getLocalString("loading_types")
233: + "(" + i + ")");
234: Session.getInstance().fireTaskStartEvent(se);
235: eventPending = true;
236: }
237:
238: Element typeElement = (Element) typeList.item(i);
239:
240: String name = typeElement.getAttributes().getNamedItem(
241: "name").getNodeValue();
242: String group = typeElement.getAttributes()
243: .getNamedItem("group").getNodeValue();
244: String blueprintPath = XMLUtil
245: .getOptionalAttributeValue(typeElement,
246: "blueprintPath", null);
247: String classImpl = XMLUtil.getOptionalAttributeValue(
248: typeElement, "classImpl");
249:
250: // is this type extending another one?
251: String super Type = null;
252: if (XMLUtil.tagExists(typeElement, "extends")) {
253: super Type = typeElement.getElementsByTagName(
254: "extends").item(0).getAttributes()
255: .getNamedItem("type").getNodeValue();
256: }
257: if (XMLUtil.tagExists(typeElement, "restricts")) {
258: super Type = typeElement.getElementsByTagName(
259: "restricts").item(0).getAttributes()
260: .getNamedItem("type").getNodeValue();
261: }
262: Element metaElement = (Element) typeElement
263: .getElementsByTagName("meta-info").item(0);
264:
265: String gui = XMLUtil.getOptionalAttributeValue(
266: metaElement, "gui", null);
267: String icon = XMLUtil.getOptionalAttributeValue(
268: metaElement, "icon");
269: boolean needsSecureTransfer = Boolean.valueOf(
270: XMLUtil.getAttributeValue(metaElement,
271: "needsSecureTransfer")).booleanValue();
272:
273: // generate type properties
274: Map<String, TypeProperty> propertyMap = new HashMap<String, TypeProperty>();
275: NodeList properties = typeElement
276: .getElementsByTagName("property");
277: for (int j = 0; j < properties.getLength(); j++) {
278: Element propertyElement = (Element) properties
279: .item(j);
280: String propertyName = propertyElement
281: .getAttributes().getNamedItem("name")
282: .getNodeValue();
283: String propertyType = propertyElement
284: .getAttributes().getNamedItem("type")
285: .getNodeValue();
286: String constraints = propertyElement
287: .getAttributes()
288: .getNamedItem("constraints").getNodeValue();
289: String mode = propertyElement.getAttributes()
290: .getNamedItem("mode").getNodeValue();
291: String propertyGroup = propertyElement
292: .getAttributes().getNamedItem("group")
293: .getNodeValue();
294: boolean isFinal = Boolean.valueOf(
295: propertyElement.getAttributes()
296: .getNamedItem("isFinal")
297: .getNodeValue()).booleanValue();
298: boolean isRequired = Boolean.valueOf(
299: propertyElement.getAttributes()
300: .getNamedItem("isRequired")
301: .getNodeValue()).booleanValue();
302: String propertyValue = XMLUtil
303: .getContent(propertyElement);
304: TypeProperty typeProperty = new TypeProperty(
305: propertyName, propertyType, constraints,
306: mode, propertyValue, propertyGroup,
307: isFinal, isRequired);
308: propertyMap.put(propertyName, typeProperty);
309: }
310: // finally generate types with properties
311: try {
312: Type type = new Type(name, icon, gui,
313: needsSecureTransfer, group, super Type,
314: classImpl, propertyMap, blueprintPath);
315: NodeList descriptionList = metaElement
316: .getElementsByTagName("description");
317: for (int j = 0; j < descriptionList.getLength(); j++) {
318: Element descriptionElement = (Element) descriptionList
319: .item(j);
320: if (descriptionElement != null) {
321: String descrLang = XMLUtil
322: .getAttributeValue(
323: descriptionElement, "lang");
324: String descrtitle = XMLUtil
325: .getAttributeValue(
326: descriptionElement, "title");
327: String descrText = XMLUtil
328: .getContent(descriptionElement);
329: String helpURL = XMLUtil.getAttributeValue(
330: descriptionElement, "help");
331: type.addDescription(descrLang, descrtitle,
332: descrText, helpURL);
333: }
334: }
335: loadedTypes.put(name, type);
336: } catch (TypeCreationException tce) {
337: tce.printStackTrace();
338: }
339: }
340: synchronized (types) {
341: synchronized (renameableTypes) {
342: synchronized (composedTypes) {
343: // XXX this is slow, but needed to ensure correct
344: // locking by keeping types object
345: // fastest solution would be "types = loadedTypes;", but
346: // is not correct as there always
347: // is some amount of time between locking and assignment
348: types.clear();
349: types.putAll(loadedTypes);
350: renameableTypes.clear();
351: // create deferred type properties
352: for (Iterator i = loadedTypes.values()
353: .iterator(); i.hasNext();) {
354: Type type = (Type) i.next();
355: List<Map<String, TypeProperty>> propertyMaps = new Vector<Map<String, TypeProperty>>();
356: if (type.getSuperTypeName() != null) {
357: // step up the supertypes to add all
358: // properties...
359: addSuperTypeProperties(type,
360: propertyMaps);
361: }
362: // add properties in backward direction
363: for (int j = propertyMaps.size(); j > 0; j--) {
364: type
365: .addInheritedProperties((Map<String, TypeProperty>) propertyMaps
366: .get(j - 1));
367: }
368: type.addInheritedProperties(type
369: .getPropertyMap());
370: // Only allow renaming types from writeable packages
371: try {
372: PackageManager pm = PackageManager
373: .getInstance();
374: PackageManager.Package pkg = pm
375: .getPackage(type);
376: if (!pkg.isReadOnly()) {
377: renameableTypes.put(type.getName(),
378: type);
379: }
380: } catch (PackageNotFoundException pnfe) {
381: logger
382: .fine("No package found for type "
383: + type.getName());
384: }
385: if (type.isBlueprint()) {
386: composedTypes.put(type.getName(), type);
387: }
388: }
389: }
390: }
391: }
392: } finally {
393: if (eventPending) {
394: TaskFinishedEvent fe = new TaskFinishedEvent(this );
395: Session.getInstance().fireTaskFinishEvent(fe);
396: }
397: TaskFinishedEvent finishedEvent = new TaskFinishedEvent(
398: this );
399: Session.getInstance().fireTaskFinishEvent(finishedEvent);
400: }
401: }
402:
403: private void addSuperTypeProperties(Type type,
404: List<Map<String, TypeProperty>> propertyMaps) {
405: Type super type;
406: String super TypeName = type.getSuperTypeName();
407: synchronized (types) {
408: super type = (Type) types.get(super TypeName);
409: }
410:
411: if (super type == null) {
412: logger.log(Level.WARNING, "Could not retrieve supertype "
413: + super TypeName + " of type " + type.getName());
414: } else {
415: propertyMaps.add(super type.getPropertyMap());
416: if (super type.getSuperTypeName() != null) {
417: addSuperTypeProperties(super type, propertyMaps);
418: }
419: }
420: }
421:
422: // //////////////////////////////////////////////////////
423: // EVENT DISPACHTING MASTER PART
424:
425: public void addTypeEventListener(TypeEventListener listener,
426: boolean usesSwing) {
427: addListener(listener, TypeEventListener.class, usesSwing);
428: }
429:
430: public void removeTypeEventListener(TypeEventListener listener) {
431: removeListener(listener, TypeEventListener.class);
432: }
433:
434: public void fireTypeCreateEvent(TypeEvent event) {
435: try {
436: load();
437: } catch (RemoteActionException e) {
438: ExceptionDialog.show(e);
439: }
440: String typeName = event.getTypeName();
441: try {
442: Type type = TypeFactory.getInstance().getType(typeName);
443: event.setBlueprintPath(type.getBlueprintPath());
444: } catch (TypeNotFoundException e) {
445: logger.log(Level.WARNING,
446: "Could not find type for type event: " + event);
447: }
448:
449: // finally inform listeners
450: addEvent(event);
451: }
452:
453: public void fireTypeChangeEvent(TypeEvent event) {
454: String typeName = event.getTypeName();
455: try {
456: Type type = TypeFactory.getInstance().getType(typeName);
457: event.setBlueprintPath(type.getBlueprintPath());
458: } catch (TypeNotFoundException e) {
459: logger.log(Level.WARNING,
460: "Could not find type for type event: " + event);
461: }
462: try {
463: load();
464: } catch (RemoteActionException e) {
465: ExceptionDialog.show(e);
466: }
467:
468: // finally inform listeners
469: addEvent(event);
470: }
471:
472: public void fireTypeDeleteEvent(TypeEvent event) {
473: String typeName = event.getTypeName();
474: try {
475: Type type = TypeFactory.getInstance().getType(typeName);
476: event.setBlueprintPath(type.getBlueprintPath());
477: } catch (TypeNotFoundException e) {
478: logger.log(Level.WARNING,
479: "Could not find type for type event: " + event);
480: }
481:
482: types.remove(typeName);
483:
484: // finally inform listeners
485: addEvent(event);
486: }
487:
488: protected void notifyListener(ContelligentEvent event,
489: ContelligentEventListener listener, Class listenerType) {
490: if (listenerType == TypeEventListener.class) {
491: TypeEventListener l = (TypeEventListener) listener;
492: if (event instanceof TypeEvent) {
493: TypeEvent e = (TypeEvent) event;
494: if (e.getType() == TypeEvent.TYPE_CHANGED) {
495: l.onTypeChanged(e);
496: } else if (e.getType() == TypeEvent.TYPE_CREATED) {
497: l.onTypeCreated(e);
498: } else if (e.getType() == TypeEvent.TYPE_DELETED) {
499: l.onTypeDeleted(e);
500: }
501: }
502: }
503: }
504:
505: }
|