001: package net.xoetrope.xui;
002:
003: import java.io.StringReader;
004: import java.util.Hashtable;
005: import java.util.ResourceBundle;
006: import java.util.Vector;
007:
008: import java.awt.BorderLayout;
009: import java.awt.CardLayout;
010: import java.awt.Component;
011: import java.awt.Container;
012: import java.awt.FlowLayout;
013: import java.awt.GridBagLayout;
014: import java.awt.GridLayout;
015: import java.awt.LayoutManager;
016: import java.awt.ScrollPane;
017:
018: import net.xoetrope.debug.DebugLogger;
019: import net.xoetrope.xml.XmlElement;
020: import net.xoetrope.xml.XmlSource;
021: import net.xoetrope.xui.build.BuildProperties;
022: import net.xoetrope.xui.data.XModel;
023:
024: /**
025: * A component factory. The factory is designed to create components
026: * for a null layout. The factory will use an incrementing id to name each component.
027: * When an XPanel is added it will automatically become the parent for subsequent
028: * components added using the factory. If another parent component is needed then
029: * the parent can be explicitly set.
030: * <br>
031: * When components are added their size is checked against that of the parent and
032: * reduced if they extend beyond the bounds of the parent.
033: * <br>
034: * The component factory can be extended by registering new XComponentConstructors.
035: * These constructors are invoked if the build in constructors cannot build the
036: * specified type.
037: * <b>
038: * Components can be specified by a type name, an type constant or by a class name.
039: * The type constants for the built-in components are specified in XPage so as
040: * to make referencing the constants easier in the client code (subclasses of XPage).
041: * <p>Copyright (c) Xoetrope Ltd., 2002-2003</p>
042: * <p>License: see license.txt</p>
043: * $Revision: 1.28 $
044: */
045: public class XComponentFactory {
046: protected static Vector componentFactories = new Vector();
047: protected static Hashtable typeNames;
048: protected static XLayoutHelper layoutHelper = new XLayoutHelper();
049:
050: protected String basePackageName;
051: protected ResourceBundle languageResourceBundle;
052:
053: /**
054: * Constructs a Component factory
055: */
056: public XComponentFactory(String packageName) {
057: if (packageName == null)
058: packageName = XPage.XUI_AWT_PACKAGE;
059:
060: basePackageName = packageName + "." + "X";
061: setupTypeNames();
062: }
063:
064: /**
065: * Set the resource bundle for this component
066: * @param resourceBundleName
067: */
068: public void setResourceBundle(String resourceBundleName) {
069: languageResourceBundle = XResourceManager
070: .getResourceBundle(resourceBundleName);
071: }
072:
073: /**
074: * A generic factory for constructing XComponents.
075: * @param type a constant identifying the type of component to be created
076: * @param content the component text/content
077: */
078: public Component constructComponent(int type, String content) {
079: Component comp = null;
080: try {
081: switch (type) {
082: case XPage.XPANEL:
083: comp = (Component) Class.forName(
084: basePackageName + XPage.PANEL).newInstance();
085: if (parentPanel == null)
086: parentPanel = (Container) comp;
087: break;
088:
089: case XPage.XLABEL:
090: comp = (Component) Class.forName(
091: basePackageName + XPage.LABEL).newInstance();
092: ((XTextHolder) comp).setText(translate(content));
093: break;
094:
095: case XPage.XRADIO:
096: comp = (Component) Class.forName(
097: basePackageName + XPage.RADIO).newInstance();
098: ((XTextHolder) comp).setText(translate(content));
099: break;
100:
101: case XPage.XCHECK:
102: comp = (Component) Class.forName(
103: basePackageName + XPage.CHECK).newInstance();
104: ((XTextHolder) comp).setText(translate(content));
105: break;
106:
107: case XPage.XCOMBO:
108: comp = (Component) Class.forName(
109: basePackageName + XPage.COMBO).newInstance();
110: break;
111:
112: case XPage.XLIST:
113: comp = (Component) Class.forName(
114: basePackageName + XPage.LIST).newInstance();
115: break;
116:
117: case XPage.XIMAGE:
118: comp = (Component) Class.forName(
119: basePackageName + XPage.IMAGE).newInstance();
120: XResourceManager.getImage((XImageHolder) comp, content);
121: break;
122:
123: case XPage.XIMAGEMAP:
124: comp = (Component) Class.forName(
125: basePackageName + XPage.IMAGEMAP).newInstance();
126: ((XImageHolder) comp).setImage(XResourceManager
127: .getImage(content));
128: break;
129:
130: case XPage.XEDIT:
131: comp = (Component) Class.forName(
132: basePackageName + XPage.EDIT).newInstance();
133: ((XTextHolder) comp).setText(translate(content));
134: break;
135:
136: case XPage.XTEXTAREA:
137: comp = (Component) Class.forName(
138: basePackageName + XPage.TEXTAREA).newInstance();
139: ((XTextHolder) comp).setText(translate(content));
140: break;
141:
142: case XPage.XPASSWORD:
143: comp = (Component) Class.forName(
144: basePackageName + XPage.PASSWORD).newInstance();
145: ((XTextHolder) comp).setText(translate(content));
146: break;
147:
148: case XPage.XBUTTON:
149: comp = (Component) Class.forName(
150: basePackageName + XPage.BUTTON).newInstance();
151: ((XTextHolder) comp).setText(translate(content));
152: break;
153:
154: case XPage.XMETACONTENT:
155: comp = (Component) Class.forName(
156: basePackageName + XPage.METACONTENT)
157: .newInstance();
158: if (content != null)
159: setMetaContent(comp, content);
160: break;
161:
162: case XPage.XSCROLLPANE:
163: comp = (Component) Class.forName(
164: basePackageName + XPage.SCROLLPANE)
165: .newInstance();
166: break;
167:
168: case XPage.XSCROLLABLEMETACONTENT:
169: comp = (Component) Class.forName(
170: basePackageName + XPage.SCROLLABLEMETACONTENT)
171: .newInstance();
172: setMetaContent(comp, content);
173: break;
174:
175: case XPage.XHOTSPOTIMAGE:
176: comp = (Component) Class.forName(
177: basePackageName + XPage.HOTSPOTIMAGE)
178: .newInstance();
179: ((XImageHolder) comp).setImage(XResourceManager
180: .getImage(content));
181: break;
182:
183: case XPage.XTABLE:
184: comp = (Component) Class.forName(
185: basePackageName + XPage.TABLE).newInstance();
186: setTableContent(comp, content);
187: break;
188:
189: case XPage.XWMF:
190: comp = (Component) Class.forName(
191: "net.xoetrope.xui.wmf.XWmf").newInstance();
192: ((XTextHolder) comp).setText(translate(content));
193: break;
194:
195: case XPage.XTABPANEL:
196: comp = (Component) Class.forName(
197: basePackageName + XPage.TABPANEL).newInstance();
198: break;
199:
200: case XPage.XSPLITPANE:
201: comp = (Component) Class.forName(
202: basePackageName + XPage.SPLITPANE)
203: .newInstance();
204: break;
205:
206: default:
207: if (type >= 0)
208: comp = buildRegisteredComponent(type, content);
209: break;
210: }
211: } catch (Exception e) {
212: e.printStackTrace();
213: }
214: return comp;
215: }
216:
217: /**
218: * Look up the translation of a key using the current language resource
219: * @param key the key string
220: * @return the translation
221: */
222: public String translate(String key) {
223: if ((key != null) && (languageResourceBundle != null)) {
224: try {
225: String trans = languageResourceBundle.getString(key);
226: if (trans != null)
227: return trans;
228: } catch (Exception ex) {
229: if (BuildProperties.DEBUG)
230: DebugLogger
231: .logWarning("No translation found for the key: "
232: + key);
233: }
234: }
235: return key;
236: }
237:
238: /**
239: * A generic factory for adding XComponents. The component is constructed,
240: * positioned and added to the parent panel if one exists. This method
241: * delegates to the registered component factories if any. All built in
242: * components are constructed with an ID.
243: * When a ScrollPane is addd it becomes the parent.
244: * @param type a name identifying the type of component to be created
245: * @param x the left coordinate
246: * @param y the top coordinate
247: * @param w the width
248: * @param h the height
249: * @param content the component text/content
250: */
251: public Component addComponent(String type, int x, int y, int w,
252: int h, String content) {
253: Component comp = null;
254: try {
255: comp = constructComponent(getTypeCode(type), content);
256: if (comp == null)
257: comp = buildRegisteredComponent(type, content);
258:
259: if (comp == null) {
260: comp = (Component) Class.forName(type).newInstance();
261: if (comp instanceof XTextHolder)
262: ((XTextHolder) comp).setText(content);
263: }
264: if (comp == null)
265: return null;
266:
267: if (parentW > 0) {
268: if (!(parentPanel instanceof ScrollPane))
269: comp.setBounds(x, y, Math.min(w, parentW - x), Math
270: .min(h, parentH));
271: } else if (w > 0)
272: comp.setBounds(x, y, w, h);
273:
274: if (comp != null) {
275: addComponent(comp);
276: return comp;
277: }
278: } catch (Exception e) {
279: e.printStackTrace();
280: }
281: return comp;
282: }
283:
284: /**
285: * A generic factory for adding XComponents. The component is constructed,
286: * positioned and added to the parent panel if one exists. This method
287: * delegates to the registered component factories if any. All built in
288: * components are constructed with an ID.
289: * When a ScrollPane is addd it becomes the parent.
290: * @param type a name identifying the type of component to be created
291: * @param x the left coordinate
292: * @param y the top coordinate
293: * @param w the width
294: * @param h the height
295: * @param content the component text/content
296: */
297: public Component addComponent(String type, Object pos,
298: String content) {
299: Component comp = null;
300:
301: try {
302: comp = constructComponent(getTypeCode(type), content);
303: if (comp == null) {
304: comp = buildRegisteredComponent(type, content);
305: }
306: if (comp != null) {
307: if (pos != null)
308: addComponent(comp, pos);
309: else
310: addComponent(comp);
311:
312: return comp;
313: }
314: } catch (Exception e) {
315: e.printStackTrace();
316: }
317: return comp;
318: }
319:
320: /**
321: * A generic factory for adding registered components via the
322: * XComponentConstructor interface or component factories.
323: * @param type a constant identifying the type of component to be created
324: * @param x the left coordinate
325: * @param y the top coordinate
326: * @param w the width
327: * @param h the height
328: * @param content the component text/content
329: */
330: protected Component buildRegisteredComponent(int type,
331: String content) {
332: int numFactories = componentFactories.size();
333: for (int i = 0; i < numFactories; i++) {
334: XComponentConstructor factory = (XComponentConstructor) componentFactories
335: .elementAt(i);
336: Component ret = factory.constructComponent(this , type,
337: content);
338: return ret;
339: }
340: return null;
341: }
342:
343: /**
344: * A generic factory for adding registered components via the
345: * XComponentConstructor interface or component factories.
346: * @param type a name identifying the type of component to be created
347: * @param x the left coordinate
348: * @param y the top coordinate
349: * @param w the width
350: * @param h the height
351: * @param content the component text/content
352: */
353: protected Component buildRegisteredComponent(String type,
354: String content) {
355: int numFactories = componentFactories.size();
356: for (int i = 0; i < numFactories; i++) {
357: XComponentConstructor factory = (XComponentConstructor) componentFactories
358: .elementAt(i);
359: Component comp = factory.constructComponent(this , type,
360: content);
361: if (comp != null) {
362: addComponent(comp);
363: return comp;
364: }
365: }
366: return null;
367: }
368:
369: /**
370: * Add a componentFactory to the static register of component constructors
371: * @param factory the new componentFactory
372: */
373: public static void registerComponentFactory(
374: XComponentConstructor factory) {
375: componentFactories.addElement(factory);
376: }
377:
378: /**
379: * Notify the component factories that some of their settings may have changed
380: * @param factory the new componentFactory
381: */
382: public static void updateComponentFactories() {
383: int numFactories = componentFactories.size();
384: for (int i = 0; i < numFactories; i++) {
385: ((XComponentConstructor) componentFactories.elementAt(i))
386: .update();
387: }
388: }
389:
390: /**
391: * Get the component factories
392: * @return a the factor store.
393: */
394: public static Vector getFactories() {
395: return componentFactories;
396: }
397:
398: /**
399: * A generic factory for adding Components. The component is created dynamically
400: * positioned and added to the parent panel if one exists. The component is
401: * named with a counter value to uniquely identify the control.
402: * @param className String value of the classname to be created
403: * @param x the left coordinate
404: * @param y the top coordinate
405: * @param w the width
406: * @param h the height
407: */
408: public Component addClass(String className, int x, int y, int w,
409: int h) {
410: Component comp = null;
411: try {
412: comp = (Component) Class.forName(className).newInstance();
413: comp.setBounds(x, y, w, h);
414: addComponent(comp);
415: } catch (Exception e) {
416: if (BuildProperties.DEBUG)
417: DebugLogger.logError("Unable to add component"
418: + className);
419: }
420: return comp;
421: }
422:
423: /**
424: * Add a non-component object to the panel or an element of the panel. This method
425: * is invoked is the XuiBuilder fails to construct a component for an XML element
426: * @param type the object type
427: * @param name a name identifying the element to be created
428: * @param content the component text/content
429: * @param attribs the element attributes if any
430: */
431: public Object addElement(String type, String name, String content,
432: Hashtable attribs) {
433: int numFactories = componentFactories.size();
434: for (int i = 0; i < numFactories; i++) {
435: XComponentConstructor factory = (XComponentConstructor) componentFactories
436: .elementAt(i);
437: Object obj = factory.addElement(this , type, name, content,
438: attribs);
439: if (obj != null)
440: return obj;
441: }
442: return null;
443: }
444:
445: /**
446: * Add a component to the panel.
447: * @param c the component to add
448: */
449: public void addComponent(Component c) {
450: if (c != parentPanel)
451: parentPanel.add(c);
452: }
453:
454: /**
455: * Add a component to the panel.
456: * @param c the component to add
457: * @param constraint the layout manager constraint
458: */
459: public void addComponent(Component c, Object constraint) {
460: if (c != parentPanel)
461: parentPanel.add(c, constraint);
462: }
463:
464: /**
465: * Sets a LayoutManager for the panel
466: * @param cont the container whose layout manager is being set or null to set the parent panel's layout manager
467: * @param type the layout manager as defined in the XLayoutHelper class
468: */
469: public LayoutManager addLayout(Container cont, int type) {
470: if (cont == null)
471: cont = parentPanel;
472: return layoutHelper.addLayout(cont, type);
473: }
474:
475: /**
476: * Change the parent for new components. This method will affect the next component added.
477: * @param c the new parent, this should be an instance of java.awt.Container
478: */
479: public void setParentComponent(Component c) {
480: parentPanel = (Container) c;
481: if (parentPanel != null) {
482: parentW = parentPanel.getSize().width;
483: parentH = parentPanel.getSize().height;
484: }
485: }
486:
487: /**
488: * Get the current parent component
489: * @return the parent component.
490: */
491: public Component getParentComponent() {
492: return parentPanel;
493: }
494:
495: /**
496: * Get the layout helper
497: * @return the layout helper
498: */
499: public static XLayoutHelper getLayoutHelper() {
500: return layoutHelper;
501: }
502:
503: /**
504: * Set the layout helper
505: * @param newHelper the new layout helper
506: */
507: public static void setLayoutHelper(XLayoutHelper newHelper) {
508: layoutHelper = newHelper;
509: }
510:
511: /**
512: * Set the content for XMetaContentHolder objects
513: * @param comp
514: * @param content
515: */
516: private void setMetaContent(Component comp, String content) {
517: if (content == null)
518: return;
519:
520: String res = translate(content);
521: boolean useContent = false;
522:
523: if (res.compareTo(content) != 0) {
524: content = res;
525: useContent = true;
526: }
527: MetaContentReader cr = new MetaContentReader(comp, content,
528: useContent);
529: cr.start();
530: }
531:
532: /**
533: * Set the content for XTable objects
534: * @param comp
535: * @param content
536: */
537: private void setTableContent(Component comp, String content) {
538: if (content != null) {
539: XModel xm = (XModel) XProjectManager.getModel()
540: .get(content);
541: ((XModelHolder) comp).setModel(xm);
542: }
543: }
544:
545: /**
546: * Get the type constant associated with a type name
547: * @param typeName the type name
548: * @return the type constant
549: */
550:
551: public static int getTypeCode(String typeName) {
552: String key;
553: setupTypeNames();
554: if (typeName.indexOf('.') > 0)
555: typeName = typeName
556: .substring(typeName.lastIndexOf('.') + 1);
557: if (typeName.charAt(0) == 'X')
558: key = typeName.substring(1, typeName.length());
559: else
560: key = typeName;
561:
562: Object retObj = typeNames.get(key.toUpperCase());
563: if (retObj == null)
564: return -1;
565:
566: return ((Integer) retObj).intValue();
567: }
568:
569: /**
570: * Setup a hashtable of type names. This will be moved to a helper class at some stage
571: */
572: protected static void setupTypeNames() {
573: if (typeNames == null) {
574: typeNames = new Hashtable(34);
575: addTypeName(XPage.PANEL, XPage.XPANEL);
576: addTypeName(XPage.LABEL, XPage.XLABEL);
577: addTypeName(XPage.RADIO, XPage.XRADIO);
578: addTypeName("RADIOBUTTON", XPage.XRADIO);
579: addTypeName(XPage.CHECK, XPage.XCHECK);
580: addTypeName("CHECK", XPage.XCHECK);
581: addTypeName(XPage.COMBO, XPage.XCOMBO);
582: addTypeName("COMBO", XPage.XCOMBO);
583: addTypeName(XPage.LIST, XPage.XLIST);
584: addTypeName(XPage.IMAGE, XPage.XIMAGE);
585: addTypeName(XPage.IMAGEMAP, XPage.XIMAGEMAP);
586: addTypeName(XPage.EDIT, XPage.XEDIT);
587: addTypeName(XPage.TEXTAREA, XPage.XTEXTAREA);
588: addTypeName(XPage.BUTTON, XPage.XBUTTON);
589: addTypeName(XPage.METACONTENT, XPage.XMETACONTENT);
590: addTypeName(XPage.GROUP, XPage.XGROUP);
591: addTypeName(XPage.SCROLLPANE, XPage.XSCROLLPANE);
592: addTypeName(XPage.SCROLLABLEMETACONTENT,
593: XPage.XSCROLLABLEMETACONTENT);
594: addTypeName(XPage.HOTSPOTIMAGE, XPage.XHOTSPOTIMAGE);
595: addTypeName("HOTSPOT", XPage.XHOTSPOTIMAGE);
596: addTypeName(XPage.TABLE, XPage.XTABLE);
597: addTypeName(XPage.WMF, XPage.XWMF);
598: addTypeName(XPage.MENUBAR, XPage.XMENUBAR);
599: addTypeName(XPage.MENU, XPage.XMENU);
600: addTypeName(XPage.MENUITEM, XPage.XMENUITEM);
601: addTypeName(XPage.PASSWORD, XPage.XPASSWORD);
602: addTypeName(XPage.UNKNOWN, XPage.XUNKNOWN);
603: addTypeName(XPage.TABPANEL, XPage.XTABPANEL);
604: addTypeName(XPage.SPLITPANE, XPage.XSPLITPANE);
605: }
606: }
607:
608: /**
609: * Add a type name to the table of types. The type names are used to recognize
610: * the xml elements in the page description
611: * @param name the type name
612: * @param value the corresponding id
613: */
614: private static void addTypeName(String name, int value) {
615: typeNames.put(name.toUpperCase(), new Integer(value));
616: }
617:
618: protected Object currentMenuBar;
619: protected Object currentMenu;
620: protected Container parentPanel;
621: protected int parentW, parentH;
622: }
623:
624: /**
625: * Description: Loads the meta content in a background thread</p>
626: */
627: class MetaContentReader extends Thread {
628: private String content;
629: private Component comp;
630: private boolean useContent;
631:
632: /**
633: * Construct and initialize a new MetaContentReader
634: * @param c the MetaContent component
635: * @param source the resource name of the content file.
636: */
637: MetaContentReader(Component c, String source, boolean useCont) {
638: useContent = useCont;
639: comp = c;
640: content = source;
641: }
642:
643: public void run() {
644: try {
645: if (content != null) {
646: XmlElement src;
647: if (!useContent)
648: src = XmlSource.read(XResourceManager
649: .getBufferedReader(content, null));
650: else {
651: StringReader reader = new StringReader(content);
652: src = XmlSource.read(reader);
653: }
654: ((XMetaContentHolder) comp).setContent(content, src);
655: }
656: } catch (Exception ex) {
657: DebugLogger
658: .logWarning("Unable to load content: " + content);
659: DebugLogger
660: .logWarning("Please check the case of the file name");
661: ex.printStackTrace();
662: }
663: }
664: }
|