0001: package net.xoetrope.xui;
0002:
0003: import java.lang.reflect.Method;
0004: import java.util.Hashtable;
0005: import java.util.Vector;
0006:
0007: import java.awt.AWTEvent;
0008: import java.awt.Component;
0009: import java.awt.Container;
0010: import java.awt.Cursor;
0011: import java.awt.Dimension;
0012: import java.awt.Graphics;
0013: import java.awt.Rectangle;
0014: import java.awt.event.FocusEvent;
0015:
0016: import net.xoetrope.debug.DebugLogger;
0017: import net.xoetrope.xui.build.BuildProperties;
0018: import net.xoetrope.xui.data.XBaseModel;
0019: import net.xoetrope.xui.data.XDataBinding;
0020: import net.xoetrope.xui.data.XModel;
0021: import net.xoetrope.xui.helper.XuiUtilities;
0022: import net.xoetrope.xui.style.XStyleFactory;
0023: import net.xoetrope.xui.validation.XValidationExceptionHandler;
0024: import net.xoetrope.xui.validation.XValidationFactory;
0025: import net.xoetrope.xui.validation.XValidationHandler;
0026: import net.xoetrope.xui.validation.XValidator;
0027:
0028: /**
0029: * <p>A basic unit for building applications. XPage integrates with
0030: * page and event management facilities provided by XUI. The page also provides
0031: * a number of methods to help control the page lifecycle.
0032: * </p><p>
0033: * Pages can be displayed using the XPageDisplay interface implemented by
0034: * XApplet and invoked by the XPage manager. Pages can be constructed directly
0035: * or via the XuiBuilder.
0036: * </p>
0037: * <p>Copyright (c) Xoetrope Ltd., 2002-2004</p>
0038: * <p>License: see license.txt</p>
0039: * $Revision: 1.33 $
0040: */
0041: public class XPage extends Container implements
0042: XValidationExceptionHandler {
0043: public static final String XUI_AWT_PACKAGE = "net.xoetrope.awt";
0044: public static final String XUI_SWING_PACKAGE = "net.xoetrope.swing";
0045:
0046: // Built-in component types
0047: public final static int XUNKNOWN = -1;
0048: public final static int XPANEL = 0;
0049: public final static int XLABEL = 1;
0050: public final static int XRADIO = 2;
0051: public final static int XCHECK = 3;
0052: public final static int XCOMBO = 4;
0053: public final static int XLIST = 5;
0054: public final static int XIMAGE = 6;
0055: public final static int XEDIT = 7;
0056: public final static int XBUTTON = 8;
0057: public final static int XMETACONTENT = 9;
0058: public final static int XGROUP = 10;
0059: public final static int XSCROLLPANE = 11;
0060: public final static int XSCROLLABLEMETACONTENT = 12;
0061: public final static int XHOTSPOTIMAGE = 13;
0062: public final static int XTABLE = 14;
0063: public final static int XWMF = 15;
0064: public final static int XANNOTATEDIMAGE = 16; // DROPPED -merged with imagemap
0065: public final static int XMENUBAR = 17;
0066: public final static int XMENU = 18;
0067: public final static int XMENUITEM = 19;
0068: public final static int XTEXTAREA = 20;
0069: public final static int XPASSWORD = 21;
0070: public final static int XIMAGEMAP = 22;
0071: public final static int XTABPANEL = 23;
0072: public final static int XSPLITPANE = 24;
0073:
0074: public final static String UNKNOWN = "Unknown";
0075: public final static String PANEL = "Panel";
0076: public final static String LABEL = "Label";
0077: public final static String RADIO = "RadioButton";
0078: public final static String CHECK = "Checkbox";
0079: public final static String COMBO = "ComboBox";
0080: public final static String LIST = "List";
0081: public final static String IMAGE = "Image";
0082: public final static String EDIT = "Edit";
0083: public final static String BUTTON = "Button";
0084: public final static String METACONTENT = "MetaContent";
0085: public final static String GROUP = "Group";
0086: public final static String SCROLLPANE = "ScrollPane";
0087: public final static String SCROLLABLEMETACONTENT = "ScrollableMetaContent";
0088: public final static String HOTSPOTIMAGE = "HotspotImage";
0089: public final static String TABLE = "Table";
0090: public final static String WMF = "Wmf";
0091: public final static String ANNOTATEDIMAGE = "AnnotatedImage"; // DROPPED -merged with imagemap
0092: public final static String MENUBAR = "MenuBar";
0093: public final static String MENU = "Menu";
0094: public final static String MENUITEM = "MenuItem";
0095: public final static String TEXTAREA = "TextArea";
0096: public final static String PASSWORD = "Password";
0097: public final static String IMAGEMAP = "ImageMap";
0098: public final static String TABPANEL = "TabPanel";
0099: public final static String SPLITPANE = "SplitPane";
0100:
0101: // Built-in LayoutManager types
0102: public final static int NULL_LAYOUT = 0;
0103: public final static int BORDER_LAYOUT = 1;
0104: public final static int FLOW_LAYOUT = 2;
0105: public final static int CARD_LAYOUT = 3;
0106: public final static int GRID_LAYOUT = 4;
0107: public final static int GRIDBAG_LAYOUT = 5;
0108:
0109: /**
0110: * This type of layout simulates the Swing layout by using a GridLayout such
0111: * that the layoutStyle attribute indicates vertical or horizontal layout. A
0112: * layoutStyle of '0' indicates a vertical layout and any other value indicates
0113: * a horizontal layout.
0114: */
0115: public final static int BOX_LAYOUT = 6;
0116:
0117: // Page states
0118: public final static int UNKNOWN_PAGE_STATE = 0;
0119: public final static int LOADED = 1;
0120: public final static int CREATED = 2;
0121: public final static int ACTIVATED = 3;
0122: public final static int DEACTIVATED = 4;
0123: public final static int DISCARDED = 5;
0124:
0125: protected static Cursor hand = Cursor
0126: .getPredefinedCursor(Cursor.HAND_CURSOR);
0127:
0128: protected XStyleFactory componentFactory;
0129:
0130: protected Vector modelBindings;
0131: protected Vector hiddenComponents;
0132: protected XEventHandler eventHandler;
0133: protected XValidationHandler validationHandler;
0134: protected Hashtable attribs;
0135:
0136: protected int status;
0137: protected boolean clearPage;
0138:
0139: /**
0140: * Constructs an empty page
0141: */
0142: public XPage() {
0143: setBackground(java.awt.Color.white);
0144: setLayout(null);
0145:
0146: status = UNKNOWN_PAGE_STATE;
0147:
0148: modelBindings = new Vector();
0149: hiddenComponents = null;
0150: validationHandler = new XValidationHandler(this );
0151: eventHandler = new XEventHandler(this , validationHandler);
0152:
0153: componentFactory = new XStyleFactory(XResourceManager
0154: .getPackageName());
0155: componentFactory.setParentComponent(this );
0156:
0157: attribs = new Hashtable();
0158: clearPage = true;
0159:
0160: // Try to set the default exception handler to the page
0161: try {
0162: String peh = XProjectManager.getCurrentProject()
0163: .getStartupParam("PageExceptionHandler");
0164: if ((peh != null)
0165: && (peh.toLowerCase().compareTo("true") == 0)) {
0166: setExceptionHandler(this );
0167: }
0168: } catch (Exception ex) {
0169: }
0170: }
0171:
0172: /**
0173: * Repaint the component once it has been created
0174: */
0175: public void addNotify() {
0176: super .addNotify();
0177: invalidate();
0178: }
0179:
0180: /**
0181: * Overrides the update method so as to allow the suppression of the default
0182: * clearing of the background. If the background is not to be cleared then the
0183: * clearPage flag can be set to false using the setClearPage method.
0184: * @param g the graphics context
0185: */
0186: public void update(Graphics g) {
0187: if (clearPage)
0188: super .update(g);
0189: else
0190: paint(g);
0191: }
0192:
0193: /**
0194: * Paint the background of this component with the background color
0195: * @param g
0196: */
0197: public void paint(Graphics g) {
0198: if (isVisible()) {
0199: g.setColor(getBackground());
0200: Dimension sz = getSize();
0201: Rectangle rect = g.getClipBounds();
0202: g.fillRect(rect.x, rect.y, rect.width + 1, rect.height + 1);
0203:
0204: try {
0205: super .paint(g);
0206: } catch (Exception e) {
0207: e.printStackTrace();
0208: }
0209: }
0210: }
0211:
0212: /**
0213: * This can be called instead of the paint method above so that the background
0214: * does not get painted.
0215: * @param g the Graphics object
0216: */
0217: public void paintSuper(Graphics g) {
0218: if (isVisible()) {
0219: try {
0220: super .paint(g);
0221: } catch (Exception e) {
0222: e.printStackTrace();
0223: }
0224: }
0225: }
0226:
0227: /**
0228: * Modify the clearPage flag. This flag determines if the default behaviour is
0229: * used to update the page whereby the background is first erased and then the
0230: * content painted or alternatively if the erase is suppressed.
0231: * @param value
0232: */
0233: public void setClearPage(boolean value) {
0234: clearPage = value;
0235: }
0236:
0237: /**
0238: * Show or hide the components. In the AWT the heavyweight peers are created
0239: * visible and paint themselves once created and therefore cause problems
0240: * for page transitions.
0241: * @param container the container
0242: * @param visible the
0243: */
0244: public void showComponents(Container container, boolean visible,
0245: int recursionLevel) {
0246: if (visible) {
0247: if (hiddenComponents != null) {
0248: // Only show components hidden by this method
0249: int numComponents = hiddenComponents.size();
0250: for (int i = 0; i < numComponents; i++) {
0251: Object comp = hiddenComponents.elementAt(i);
0252: if (comp != null)
0253: ((Component) comp).setVisible(true);
0254: }
0255: hiddenComponents = null;
0256: }
0257: } else {
0258: int numComponents = container.getComponentCount();
0259: if (recursionLevel == 0)
0260: hiddenComponents = new Vector(numComponents, 4);
0261: for (int i = 0; i < numComponents; i++) {
0262: Component comp = container.getComponent(i);
0263: // Don't try to hide hidden components or the state will be lost
0264: if (comp.isVisible()) {
0265: comp.setVisible(false);
0266: hiddenComponents.addElement(comp);
0267: }
0268: if (comp instanceof Container)
0269: showComponents((Container) comp, false,
0270: ++recursionLevel);
0271: }
0272: }
0273: }
0274:
0275: //--Start of Validation-------------------------------------------------------
0276: /**
0277: * Set the validation exception handler called when a validation exception is trapped
0278: * @param eh the new event handler
0279: */
0280: public void setExceptionHandler(XValidationExceptionHandler eh) {
0281: validationHandler.setExceptionHandler(eh);
0282: }
0283:
0284: /**
0285: * A method called when a validation exeption has been trapped.
0286: *
0287: * @param comp Component being validated
0288: * @param ex The exception caused
0289: * @param validator The validator being used to validate.
0290: * @return true to continue with error validation or false to suppress further
0291: * validation.
0292: */
0293: public boolean handleException(Component comp, Exception ex,
0294: XValidator validator) {
0295: ex.printStackTrace();
0296:
0297: return true;
0298: }
0299:
0300: /**
0301: * Reset/removes all validations
0302: */
0303: public void clearValidations() {
0304: validationHandler.checkValidations();
0305: }
0306:
0307: /**
0308: * Check all validations for this page. Typically this method should be
0309: * invoked prior to a page transition or a critical transaction.
0310: * @return the maximum error level raised by the validators
0311: */
0312: public int checkValidations() {
0313: validationHandler.accumulateMessages(true, 0);
0314: int ret = validationHandler.checkValidations();
0315: ret = validationHandler.accumulateMessages(false, ret);
0316: return ret;
0317: }
0318:
0319: /**
0320: * informs the handler when a page validation is starting or stopping. Typically
0321: * when it starts the page will begin to accumulate message which are to be displayed.
0322: * When the parameter is false the page will usually display the accumulated
0323: * messages
0324: * @param start boolean to indicate whether the accumulation is started or stopped.
0325: */
0326: public int accumulateMessages(boolean start, int level) {
0327: return 0;
0328: }
0329:
0330: /**
0331: * Adds a validation to this page.
0332: * @param comp the component being validated
0333: * @param validationName the name of the validation in the validation file
0334: * @param method the method used to get the component's value if any
0335: * @param mask the event mask used to filter the events that trigger the validation
0336: * @return the new and initialized XValidator
0337: */
0338: public XValidator addValidation(Component comp,
0339: String validationName, String method, int mask) {
0340: return validationHandler.addValidation(comp, validationName,
0341: method, mask);
0342: }
0343:
0344: /**
0345: * Adds a validation to this page. It is assumed that the validation will be
0346: * invoked in response to FocusEvent.FOCUS_LOST events
0347: * @param comp the component being validated
0348: * @param validationName the name of the validation in the validation file
0349: * @param method the method used to get the component's value if any
0350: * @return the new and initialized XValidator
0351: */
0352: public XValidator addValidation(Component comp,
0353: String validationName, String method) {
0354: return addValidation(comp, validationName, method,
0355: FocusEvent.FOCUS_LOST);
0356: }
0357:
0358: /**
0359: * Adds a validation to this page. It is assumed that the validation will be
0360: * invoked in response to FocusEvent.FOCUS_LOST events
0361: * @param comp the component being validated
0362: * @param validationName the name of the validation in the validation file
0363: * @return the new and initialized XValidator
0364: */
0365: public XValidator addValidation(Component comp,
0366: String validationName) {
0367: return addValidation(comp, validationName, null,
0368: FocusEvent.FOCUS_LOST);
0369: }
0370:
0371: /**
0372: * Sets the factory used to create XValidator objects
0373: * @param vf
0374: */
0375: public void setValidationFactory(XValidationFactory vf) {
0376: validationHandler.setValidationFactory(vf);
0377: }
0378:
0379: /**
0380: * Sets the factory used to create XValidator objects. It is assumed that the
0381: * file can be found on the classpath and that it can be read as UTF8 (by default)
0382: * @param vf the filename
0383: */
0384: public void setValidationFactory(String vf) {
0385: try {
0386: setValidationFactory(new XValidationFactory(
0387: XResourceManager.getBufferedReader(vf, null)));
0388: } catch (Exception ex) {
0389: }
0390: }
0391:
0392: /**
0393: * Gets the validation handler
0394: * @return the validation handler
0395: */
0396: public XValidationHandler getValidationHandler() {
0397: return validationHandler;
0398: }
0399:
0400: /**
0401: * Invoke the validators for the last event. Multiple validations are checked
0402: * in the order in which they were added.
0403: * @return the maximum level returned by the validators
0404: */
0405: public int validationHandler() {
0406: return validationHandler.validationHandler();
0407: }
0408:
0409: //--End of Validation---------------------------------------------------------
0410:
0411: //--Start of Event Handling---------------------------------------------------
0412: /**
0413: * Get the current event handler
0414: * @return the event handler
0415: */
0416: public XEventHandler getEventHandler() {
0417: return eventHandler;
0418: }
0419:
0420: /**
0421: * Set the current event handler
0422: */
0423: public void setEventHandler(XEventHandler eh) {
0424: eventHandler = eh;
0425: }
0426:
0427: /**
0428: * Get the current event
0429: * @return the AWTEvent that was last triggered
0430: */
0431: public AWTEvent getCurrentEvent() {
0432: return eventHandler.getCurrentEvent();
0433: }
0434:
0435: /**
0436: * Adds a listener for an event type. This method should not normally be
0437: * called by an application
0438: * @param comp the component that fires events
0439: * @param listenerName the name of the listener interface
0440: * @param argType the listener arguments
0441: */
0442: public void addListener(Object comp, String listenerName,
0443: String argType) {
0444: eventHandler.addListener(comp, listenerName, argType);
0445: }
0446:
0447: /**
0448: * Adds an event handler. A specific handler such as the addActionHandler should
0449: * be used instead of calling this method
0450: * @param comp the component that fires the event
0451: * @param eventType the event ID/mask
0452: * @param methodName the method to be invoked in response to the object
0453: */
0454: public void addHandler(Component comp, long eventType,
0455: String methodName) throws Exception {
0456: eventHandler.addHandler(comp, eventType, methodName);
0457: }
0458:
0459: /**
0460: * Check the focus change status
0461: * @return true if the focus change events are being suppressed.
0462: */
0463: public boolean isFocusChangeSuppressed() {
0464: return eventHandler.isFocusChangeSuppressed();
0465: }
0466:
0467: /**
0468: * Adds a handler for action events
0469: * @param menuItem the component that fires the event
0470: * @param methodName the method to be invoked in response to the action event
0471: * @see java.awt.event.ActionListener
0472: * @see java.awt.event.ActionEvent
0473: */
0474: public void addMenuHandler(Object menuItem, String methodName) {
0475: eventHandler.addMenuHandler(menuItem, methodName);
0476: }
0477:
0478: /**
0479: * Adds a handler for action events
0480: * @param comp the component that fires the events
0481: * @param methodName the method to be invoked in response to the action event
0482: * @see java.awt.event.ActionListener
0483: * @see java.awt.event.ActionEvent
0484: */
0485: public void addActionHandler(Component comp, String methodName) {
0486: eventHandler.addActionHandler(comp, methodName);
0487: }
0488:
0489: /**
0490: * Adds a handler for focus events
0491: * @param comp the component that fires the events
0492: * @param methodName the method to be invoked in response to the focus event
0493: * @see java.awt.event.FocusListener
0494: * @see java.awt.event.FocusEvent
0495: */
0496: public void addFocusHandler(Component comp, String methodName) {
0497: eventHandler.addFocusHandler(comp, methodName);
0498: }
0499:
0500: /**
0501: * Adds a handler for text events
0502: * @param comp the component that fires the events
0503: * @param methodName the method to be invoked in response to the text event
0504: * @see java.awt.event.TextListener
0505: * @see java.awt.event.TextEvent
0506: */
0507: public void addTextHandler(Component comp, String methodName) {
0508: eventHandler.addTextHandler(comp, methodName);
0509: }
0510:
0511: /**
0512: * Adds a handler for item events
0513: * @param comp the component that fires the events
0514: * @param methodName the method to be invoked in response to the item event
0515: * @see java.awt.event.ItemListener
0516: * @see java.awt.event.ItemEvent
0517: */
0518: public void addItemHandler(Component comp, String methodName) {
0519: eventHandler.addItemHandler(comp, methodName);
0520: }
0521:
0522: /**
0523: * Adds a handler for key events
0524: * @param comp the component that fires the events
0525: * @param methodName the method to be invoked in response to the key event
0526: * @see java.awt.event.KeyListener
0527: * @see java.awt.event.KeyEvent
0528: */
0529: public void addKeyHandler(Component comp, String methodName) {
0530: eventHandler.addKeyHandler(comp, methodName);
0531: }
0532:
0533: /**
0534: * Adds a handler for mouse events
0535: * @param comp the component that fires the events
0536: * @param methodName the method to be invoked in response to the mouse event
0537: * @see java.awt.event.MouseMotionListener
0538: * @see java.awt.event.MouseEvent
0539: */
0540: public void addMouseHandler(Component comp, String methodName) {
0541: if (comp != null) {
0542: eventHandler.addMouseHandler(comp, methodName);
0543:
0544: if (comp.getClass().getName().indexOf("Button") > -1)
0545: comp.setCursor(hand);
0546: }
0547: }
0548:
0549: /**
0550: * Adds a handler for mouse motion events
0551: * @param comp the component that fires the events
0552: * @param methodName the method to be invoked in response to the mouse event
0553: * @see java.awt.event.MouseMotionListener
0554: * @see java.awt.event.MouseEvent
0555: */
0556: public void addMouseMotionHandler(Component comp, String methodName) {
0557: eventHandler.addMouseMotionHandler(comp, methodName);
0558: }
0559:
0560: /**
0561: * A utility method used to determine if the last event corrseponds to a mouse
0562: * click. The notion of a click is extended by assuming the a mouse press and
0563: * release within a single component constitutes a click even if not at the
0564: * same coordinate. A MouseEvent.MOUSE_CLICKED is only triggered when the press
0565: * and release are at the same location and this is often inadequate for end-user
0566: * interaction.
0567: * @return true if the mouse was clicked
0568: */
0569: public boolean wasMouseClicked() {
0570: return eventHandler.wasMouseClicked();
0571: }
0572:
0573: /**
0574: * A utility method used to determine if the last event corrseponds to a mouse
0575: * double click. The notion of a click is extended by assuming the a mouse press and
0576: * release within a single component constitutes a click even if not at the
0577: * same coordinate. A MouseEvent.MOUSE_CLICKED is only triggered when the press
0578: * and release are at the same location and this is often inadequate for end-user
0579: * interaction.
0580: * @return true if the mouse was double clicked
0581: */
0582: public boolean wasMouseDoubleClicked() {
0583: return eventHandler.wasMouseDoubleClicked();
0584: }
0585:
0586: /**
0587: * A utility method used to determine if the last event corrseponds to a mouse
0588: * right click. The notion of a click is extended by assuming the a mouse press and
0589: * release within a single component constitutes a click even if not at the
0590: * same coordinate. A MouseEvent.MOUSE_CLICKED is only triggered when the press
0591: * and release are at the same location and this is often inadequate for end-user
0592: * interaction.
0593: * @return true if the mouse was right clicked
0594: */
0595: public boolean wasMouseRightClicked() {
0596: return eventHandler.wasMouseRightClicked();
0597: }
0598:
0599: /**
0600: * Show the hand/pointer cursor for this component
0601: * @param comp the component
0602: */
0603: public void showHandCursor(Component comp) {
0604: comp.setCursor(hand);
0605: }
0606:
0607: //--End of Event Handling-----------------------------------------------------
0608:
0609: //--Start of Data Binding-----------------------------------------------------
0610: public Vector getBindings() {
0611: return modelBindings;
0612: }
0613:
0614: /**
0615: * Add a binding of a component to the data model. If the page has already
0616: * been activated this method will update the binding automatically.
0617: * @param b the binding
0618: */
0619: public void addBinding(XDataBinding b) {
0620: modelBindings.addElement(b);
0621: if ((status == CREATED) || (status == ACTIVATED))
0622: b.get();
0623: }
0624:
0625: /**
0626: * Remove a binding of a component to the data model.
0627: * @param b the binding
0628: */
0629: public void removeBinding(XDataBinding b) {
0630: modelBindings.removeElement(b);
0631: }
0632:
0633: /**
0634: * Update the bound model node for each binding. First the output path is
0635: * reevaluated and then updated by setting the output node. Then the source
0636: * path is reevaluated and set. Evaluation of the paths allows derived classes
0637: * to dynamically modify the bindings. Some bindings may save the selection or
0638: * state information to the output node and subsequently use it to restore the
0639: * component state. This method does not alter the data held by the bound model
0640: * nodes. To actually save the data use saveBoundComponentValues and to update
0641: * the UI use updateBoundComponentValues.
0642: */
0643: public void updateBindings() {
0644: // iterate over components and update their bindings
0645: int numBindings = modelBindings.size();
0646: for (int i = 0; i < numBindings; i++) {
0647: XDataBinding binding = ((XDataBinding) modelBindings
0648: .elementAt(i));
0649: String rawOutputPath = binding.getOutputPath();
0650: String rawSourcePath = binding.getSourcePath();
0651:
0652: if (rawOutputPath == null)
0653: rawOutputPath = rawSourcePath;
0654:
0655: if (rawOutputPath != null) {
0656: String outputPath = evaluatePath(rawOutputPath);
0657: if (outputPath.length() > 0) {
0658: if ((rawOutputPath.indexOf("${") < 0)
0659: || !rawOutputPath.equals(outputPath)) {
0660: // Strip any attribute values as the output node should not be
0661: // distinguished by the selected values
0662: String evaluatedPath = stripAttributeValues(outputPath);
0663: boolean appendByDefault = XBaseModel
0664: .getAppendByDefault();
0665: XBaseModel.setAppendByDefault(true);
0666: XModel outputNode = (XModel) XProjectManager
0667: .getModel()
0668: .get(
0669: XModel
0670: .prefixOutputPath(evaluatedPath));
0671: XBaseModel.setAppendByDefault(appendByDefault);
0672:
0673: // If an attribute had been specified then the newNode might not
0674: // have the selected value and we would need to set it or else the
0675: // bound component won't show the correct initial value
0676: if (!evaluatedPath.equals(outputPath)) {
0677: XModel valueNode = (XModel) XProjectManager
0678: .getModel()
0679: .get(
0680: XModel
0681: .prefixOutputPath(outputPath));
0682: Object outputValue = valueNode.get();
0683: if (outputValue != null)
0684: outputNode.set(outputValue);
0685: }
0686: binding.setOutput(outputNode);
0687: }
0688: }
0689: }
0690:
0691: if (rawSourcePath != null) {
0692: String sourcePath = evaluatePath(rawSourcePath);
0693: if (sourcePath.length() > 0) {
0694: if ((rawSourcePath.indexOf("${") < 0)
0695: || !rawSourcePath.equals(sourcePath)) {
0696: XModel sourceNode = (XModel) XProjectManager
0697: .getModel().get(sourcePath);
0698: binding.setSource(sourceNode);
0699: }
0700: }
0701: }
0702: }
0703: }
0704:
0705: /**
0706: * Update the UI with values from the model
0707: */
0708: public void updateBoundComponentValues() {
0709: // iterate over components and update their values
0710: int numBindings = modelBindings.size();
0711: for (int i = 0; i < numBindings; i++) {
0712: try {
0713: XDataBinding binding = ((XDataBinding) modelBindings
0714: .elementAt(i));
0715: binding.get();
0716: } catch (Exception ex) {
0717: if (BuildProperties.DEBUG)
0718: DebugLogger
0719: .logError("Unable to update the bindings: "
0720: + ex.getMessage());
0721: }
0722: }
0723: }
0724:
0725: /**
0726: * Save the component values to the model
0727: */
0728: public void saveBoundComponentValues() {
0729: // iterate over components and save their values
0730: int numBindings = modelBindings.size();
0731: for (int i = 0; i < numBindings; i++) {
0732: XDataBinding binding = ((XDataBinding) modelBindings
0733: .elementAt(i));
0734: binding.set();
0735: }
0736: }
0737:
0738: /**
0739: * Find the data binding associated with a component
0740: * @param targetComp the component whose binding is required
0741: * @return the binding or null if no binding is found
0742: */
0743: public XDataBinding getBinding(Component targetComp) {
0744: // iterate over components
0745: int numBindings = modelBindings.size();
0746: for (int i = 0; i < numBindings; i++) {
0747: XDataBinding binding = ((XDataBinding) modelBindings
0748: .elementAt(i));
0749: if (binding.getComponent() == targetComp)
0750: return binding;
0751: }
0752: return null;
0753: }
0754:
0755: /**
0756: * Find the data binding associated with a data source path
0757: * @param targetPath the path to the bound model
0758: * @return the binding or null if no binding is found
0759: */
0760: public XDataBinding getBinding(String targetPath) {
0761: // iterate over components
0762: int numBindings = modelBindings.size();
0763: for (int i = 0; i < numBindings; i++) {
0764: XDataBinding binding = ((XDataBinding) modelBindings
0765: .elementAt(i));
0766: if (binding.getSourcePath().compareTo(targetPath) == 0)
0767: return binding;
0768: }
0769: return null;
0770: }
0771:
0772: /**
0773: * Get the page status
0774: * @return the current status
0775: */
0776: public int getStatus() {
0777: return status;
0778: }
0779:
0780: /**
0781: * Set the page status
0782: * @param newStatus the new page status
0783: */
0784: public void setStatus(int newStatus) {
0785: status = newStatus;
0786: }
0787:
0788: /**
0789: * A method called once the page has been created and initialized but just
0790: * prior to display
0791: */
0792: public void pageActivated() {
0793: }
0794:
0795: /**
0796: * A method called once the page has been created but not yet initialized.
0797: */
0798: public void pageCreated() {
0799: }
0800:
0801: /**
0802: * Called when the page is about to loose scope and be hidden.
0803: */
0804: public void pageDeactivated() {
0805: }
0806:
0807: //--End of Data Binding-------------------------------------------------------
0808:
0809: //--End of Attribute Methods--------------------------------------------------
0810: /**
0811: * <p>Set a named attributes. The attributes are stored in a hashtable owned by
0812: * the page. Derived classes may access the hashtable directly but the
0813: * preferred method of access is the getAttribute method. Attributes are used
0814: * by the XuiBuilder class for component attributes other than those it handles
0815: * directly. The attributes can be thought of as component properties or extra
0816: * data and need
0817: * not be used directly by the component.</p>
0818: * <p>
0819: * Attributes are stored using a key in the form attribName_compName or just
0820: * the attribName if compName is null.
0821: * </p>
0822: * @param attribName the attribute name
0823: * @param compName the component name or null if it is a page attribute
0824: * @param attribValue the attribute value
0825: * @see #getAttribute
0826: */
0827: public void setAttribute(String attribName, String compName,
0828: Object attribValue) {
0829: if (attribValue != null)
0830: attribs.put(attribName
0831: + (compName != null ? "_" + compName : ""),
0832: attribValue);
0833: }
0834:
0835: /**
0836: * Gets an attribute value
0837: * @param attribName the name of the attribute
0838: * @return the value
0839: */
0840: public Object getAttribute(String attribName) {
0841: AWTEvent evt = getCurrentEvent();
0842: return getAttribute(attribName, evt == null ? ""
0843: : ((Component) evt.getSource()).getName());
0844: }
0845:
0846: /**
0847: * Gets an attribute value
0848: * @param attribName the name of the attribute
0849: * @param compName the component name
0850: * @return the value
0851: */
0852: public Object getAttribute(String attribName, String compName) {
0853: return attribs.get(attribName
0854: + (compName != null ? "_" + compName : ""));
0855: }
0856:
0857: /**
0858: * Get a name for a component. If the component doesn't have one use the
0859: * component hashcode
0860: * @param comp the component
0861: * @return the name
0862: */
0863: public String getComponentName(Component comp) {
0864: String compName = comp.getName();
0865: if ((compName == null) || (compName.length() == 0))
0866: compName = new Integer(comp.hashCode()).toString();
0867: return compName;
0868: }
0869:
0870: /**
0871: * Gets an attribute value
0872: * @param attribName the name of the attribute
0873: * @return the value
0874: */
0875: public Object getEventAttribute(Component c, String attribName) {
0876: return attribs.get(attribName);
0877: }
0878:
0879: /**
0880: * Find a named component in the container. Any child containers of the
0881: * container will be searched recursively till the named component is found.
0882: * The first component with a matching name will be returned.
0883: * @param name the name to locate
0884: * @return the component ornull if nothing is found
0885: */
0886: public Component findComponent(String name) {
0887: Component comp = findComponent(this , name);
0888: if ((comp == null) && BuildProperties.DEBUG)
0889: DebugLogger.logWarning("Unable to find the component: "
0890: + name);
0891:
0892: return comp;
0893: }
0894:
0895: /**
0896: * Find a named component in the container. Any child containers of the
0897: * container will be searched recursively till the named component is found.
0898: * The first component with a matching name will be returned.
0899: * @param container the page or container to search
0900: * @param name the name to locate
0901: * @return the component or null if nothing is found
0902: */
0903: public Component findComponent(Container container, String name) {
0904: int numComponents = container.getComponentCount();
0905: for (int i = 0; i < numComponents; i++) {
0906: Component comp = container.getComponent(i);
0907: if ((comp.getName() != null)
0908: && (comp.getName().compareTo(name) == 0))
0909: return comp;
0910: else if (comp instanceof Container) {
0911: Component c = findComponent((Container) comp, name);
0912: if (c != null)
0913: return c;
0914: }
0915: }
0916:
0917: return null;
0918: }
0919:
0920: /**
0921: * Evaluates an attribute value. An attribute may be a value or a method call.
0922: * If brackets are part of the value it is assumed that a method call is
0923: * intended. The method call is indicated by the '$' symbol e.g. ${myMethod()}
0924: * @param attribValue the raw attribute value
0925: * @return the evaluated attribute
0926: */
0927: public Object evaluateAttribute(String attribValue) {
0928: try {
0929: if (attribValue.indexOf("${") == 0) {
0930: Class c = this .getClass();
0931: Class[] params;
0932: int endAttribName = attribValue.indexOf('(');
0933: int endAttribArgs = attribValue.indexOf(')');
0934: String methodName = attribValue.substring(2,
0935: endAttribName);
0936: Object args[];
0937: if ((endAttribName + 1) == endAttribArgs) {
0938: args = new Object[0];
0939: params = new Class[0];
0940: } else {
0941: String argument;
0942: String argValues = attribValue.substring(
0943: endAttribName + 1, endAttribArgs);
0944: // Count the number of commas
0945: int numArgs = 1 + XuiUtilities
0946: .count(argValues, ',');
0947: args = new Object[numArgs];
0948: params = new Class[numArgs];
0949:
0950: // Extract the arguments to the appropriate array
0951: int start = 0;
0952: for (int i = 0; i < numArgs; i++) {
0953: int pos = argValues.indexOf(start);
0954: if (pos > -1) {
0955: argument = argValues.substring(start, pos);
0956: start = pos + 1;
0957: } else {
0958: argument = argValues.substring(start);
0959: // Make this to be the last iteration
0960: numArgs = 0;
0961: }
0962: argument = argument.trim();
0963: char firstChar = argument.charAt(0);
0964: // If the first char is a digit or a minus then assume it's a number
0965: if (Character.isDigit(firstChar)
0966: || (firstChar == '-')) {
0967: args[i] = new Integer(argument);
0968: params[i] = int.class;
0969: } else {
0970: args[i] = argument;
0971: params[i] = String.class;
0972: }
0973: }
0974: }
0975:
0976: // Find and invoke the methos
0977: Method theMethod = c.getMethod(methodName, params);
0978: return theMethod.invoke(this , args);
0979: }
0980: } catch (Exception ex) {
0981: if (BuildProperties.DEBUG)
0982: DebugLogger
0983: .logWarning("Unable to evaluate attribute with the method call(?): "
0984: + attribValue);
0985: }
0986: return attribValue;
0987: }
0988:
0989: /**
0990: * Evaluates a path (potentially) containing a method call
0991: * @param path the raw path
0992: * @return the evaluated path
0993: */
0994: public String evaluatePath(String path) {
0995: try {
0996: // path: /applicant/${userId()}/address
0997: int pos = path.indexOf("${");
0998: if (pos < 0)
0999: return path;
1000:
1001: int endPos = path.indexOf('}', pos);
1002: String evaluatedPath = null;
1003: if (endPos > 0)
1004: evaluatedPath = (String) evaluateAttribute(path
1005: .substring(pos, endPos));
1006: else
1007: return path;
1008: // evaluatedPath: u1234
1009:
1010: String newPath = evaluatedPath + path.substring(endPos + 1);
1011: if (pos > 0)
1012: newPath = path.substring(0, pos) + newPath;
1013: // newPath: /applicant/u1234/address
1014: return evaluatePath(newPath);
1015: } catch (Exception ex) {
1016: if (BuildProperties.DEBUG)
1017: DebugLogger
1018: .logWarning("Unable to evaluate path with the method call(?): "
1019: + path);
1020: }
1021: return path;
1022: }
1023:
1024: /**
1025: * Remove the attribute paths from a path e.g. remove @value=ignore
1026: * @param path the path to strip
1027: * @return the stripped path
1028: */
1029: public String stripAttributeValues(String path) {
1030: String newPath = path;
1031: int pos = newPath.indexOf('@');
1032: while (pos > 0) {
1033: // Check for the escape sequence
1034: int endPos = newPath.indexOf('[');
1035: if (endPos > 0) {
1036: endPos = newPath.indexOf(']', endPos);
1037: endPos++;
1038: } else
1039: endPos = newPath.indexOf('/', pos);
1040: String remainder = newPath.substring(endPos);
1041: newPath = newPath.substring(0, pos) + remainder;
1042: pos = newPath.indexOf('@');
1043: }
1044: return newPath;
1045: }
1046:
1047: /**
1048: * Get the component factory instance being used by this page.
1049: * @return the component factory
1050: */
1051: public XComponentFactory getComponentFactory() {
1052: return componentFactory;
1053: }
1054:
1055: /**
1056: * Set the component factory instance being used by this page when constructing
1057: * new pages.
1058: */
1059: public void setComponentFactory(XStyleFactory factory) {
1060: componentFactory = factory;
1061: componentFactory.setParentComponent(this );
1062: }
1063:
1064: public String translate(String key) {
1065: return componentFactory.translate(key);
1066: }
1067:
1068: //--End of Attribute Methods--------------------------------------------------
1069:
1070: //--Start of MessageBox methods-----------------------------------------------
1071: /**
1072: * Shows a modal message box
1073: * @param title the message dialog title
1074: * @param msg the text of the message
1075: */
1076: public void showMessage(String title, String msg) {
1077: showMessage(getParent(), title, msg);
1078: }
1079:
1080: /**
1081: * Shows a modal message box
1082: * @param parent the message dialog parent
1083: * @param title the message dialog title
1084: * @param msg the text of the message
1085: */
1086: public void showMessage(Container parent, String title, String msg) {
1087: new MessageThread(parent, title, msg).start();
1088: }
1089:
1090: /**
1091: * A class used to display the message box
1092: * <p>Company: Xoetrope Ltd.</p>
1093: */
1094: class MessageThread extends Thread {
1095: String msg, caption;
1096: Container container;
1097:
1098: public MessageThread(Container cont, String title,
1099: String message) {
1100: container = cont;
1101: msg = message;
1102: caption = title;
1103: }
1104:
1105: public void run() {
1106: eventHandler.suppressFocusEvents(true);
1107:
1108: try {
1109: XMessageBoxSetup msgbox = (XMessageBoxSetup) Class
1110: .forName(
1111: XResourceManager.getPackageName()
1112: + ".XMessageBox").newInstance();
1113: msgbox.setup(caption, msg, getSize(), container);
1114: } catch (Exception ex) {
1115: if (BuildProperties.DEBUG) {
1116: DebugLogger
1117: .logError("Could not create the message box: "
1118: + msg);
1119: ex.printStackTrace();
1120: }
1121: }
1122:
1123: eventHandler.suppressFocusEvents(false);
1124: }
1125: }
1126: //--End of MessageBox methods-------------------------------------------------
1127: }
|