001: // THIS SOFTWARE IS PROVIDED BY SOFTARIS PTY.LTD. AND OTHER METABOSS
002: // CONTRIBUTORS ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING,
003: // BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
004: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SOFTARIS PTY.LTD.
005: // OR OTHER METABOSS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
006: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
007: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
008: // OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
009: // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
010: // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
011: // EVEN IF SOFTARIS PTY.LTD. OR OTHER METABOSS CONTRIBUTORS ARE ADVISED OF THE
012: // POSSIBILITY OF SUCH DAMAGE.
013: //
014: // Copyright 2000-2005 © Softaris Pty.Ltd. All Rights Reserved.
015: package com.metaboss.enterprise.xi.enhydrabarracuda;
016:
017: import java.io.Serializable;
018: import java.util.Collections;
019: import java.util.HashMap;
020: import java.util.Map;
021: import java.util.TreeMap;
022:
023: import org.apache.commons.logging.Log;
024: import org.apache.commons.logging.LogFactory;
025:
026: import com.metaboss.enterprise.ui.UIInputValidationException;
027: import com.metaboss.enterprise.ui.UIUnexpectedProgramConditionException;
028:
029: /** This class represents single instance of the running application
030: * it is the ultimate parent and owner of all pages */
031: public class Application implements Serializable {
032: // Static logging instance
033: private static final Log sLogger = LogFactory
034: .getLog(Application.class);
035: // Used to generate unique instance id for each navigation. Once reached Long.MAX_VALUE will cycle to zero (if it ever gets there !)
036: private static long sNextNavigationInstanceId = System
037: .currentTimeMillis();
038: // Semaphore used in instance id generation
039: private static Object sNextNavigationInstanceIdSemaphore = new Object();
040:
041: /** This class is storing navigation details. It should be used by
042: * application elements to store / retrieve navigation related stuff
043: * Each navigation move starts from client clicking something on the html page
044: * The FromInstanceId and FromPageId attributes are uniquely identifying this page */
045: public class NavigationMove implements Serializable {
046:
047: private String mInstanceId = null; // Unique instance id of this navigation move
048: private String mFromInstanceId = null; // Unique instance id of the previous navigation move
049: private Map mNavigationParameters = new TreeMap();
050:
051: private class PageData {
052: // Map of WidgetModels for widgets on this page. Widget Id is a key
053: private Map mWidgetModels = null;
054: // Map of Maps of WidgetModels for widgets on this form. Form Id is first key,
055: // Widget Id is another. Note that form widget value is addressable separately
056: // during put operations this leaves application in control
057: private Map mFormWidgetModels = null;
058: }
059:
060: // Store for all value models for all pages in this navigation move
061: private Map mPages = null;
062:
063: /** Restrict creation from outside */
064: private NavigationMove() {
065: synchronized (sNextNavigationInstanceIdSemaphore) {
066: mInstanceId = Long.toString(
067: sNextNavigationInstanceId++,
068: Character.MAX_RADIX);
069: }
070: }
071:
072: /** Returns unique instance id for this move */
073: public String getInstanceId() {
074: return mInstanceId;
075: }
076:
077: /** Returns Map of all parameters and their values. The returned Map can not be modified. */
078: public Map getLocalParameters() {
079: return Collections.unmodifiableMap(mNavigationParameters);
080: }
081:
082: /** Returns the value of a navigation parameter as a String, or throws exception if the specified parameter does not exist.
083: * This method should only be used if caller is sure that the parameter value is always a sinlge string.
084: * For array of strings getMandatoryParameters() method should be used
085: * Takes whole chain of navs in the consideration */
086: public String getMandatoryParameter(String pParameterName)
087: throws UIInputValidationException,
088: UIUnexpectedProgramConditionException {
089: String[] lParameters = getMandatoryParameters(pParameterName);
090: if (lParameters != null && lParameters.length == 1)
091: return lParameters[0];
092: throw new UIUnexpectedProgramConditionException(
093: "Unexpected value of the mandatory navigation parameter '"
094: + pParameterName
095: + "'. Expecting single string value.");
096: }
097:
098: /** Returns the value of a navigation parameter as a String, or throws exception if the specified parameter does not exist.
099: * This method should only be used if caller is sure that the parameter value is always a sinlge string.
100: * For array of strings getMandatoryParameters() method should be used
101: * Only takes this nav in consideration. */
102: public String getLocalMandatoryParameter(String pParameterName)
103: throws UIInputValidationException,
104: UIUnexpectedProgramConditionException {
105: String[] lParameters = getLocalMandatoryParameters(pParameterName);
106: if (lParameters != null && lParameters.length == 1)
107: return lParameters[0];
108: throw new UIUnexpectedProgramConditionException(
109: "Unexpected value of the mandatory navigation parameter '"
110: + pParameterName
111: + "'. Expecting single string value.");
112: }
113:
114: /** Returns the value of a navigation parameter as a String, or throws exception if the specified parameter does not exist.
115: * Takes whole chain of navs in the consideration */
116: public String[] getMandatoryParameters(String pParameterName)
117: throws UIInputValidationException {
118: for (Application.NavigationMove lMove = this ; lMove != null; lMove = (Application.NavigationMove) (lMove.mFromInstanceId != null ? mNavigationHistory
119: .get(lMove.mFromInstanceId)
120: : null)) {
121: if (lMove.mNavigationParameters
122: .containsKey(pParameterName))
123: return (String[]) lMove.mNavigationParameters
124: .get(pParameterName);
125: }
126: throw new UIInputValidationException(
127: "Unable to read mandatory navigation parameter '"
128: + pParameterName + "'");
129: }
130:
131: /** Returns the value of a navigation parameter as a String, or throws exception if the specified parameter does not exist.
132: * Only takes this nav in consideration. */
133: public String[] getLocalMandatoryParameters(
134: String pParameterName)
135: throws UIInputValidationException {
136: if (mNavigationParameters.containsKey(pParameterName))
137: return (String[]) mNavigationParameters
138: .get(pParameterName);
139: throw new UIInputValidationException(
140: "Unable to read mandatory navigation parameter '"
141: + pParameterName + "'");
142: }
143:
144: /** Returns the value of a navigation parameter as a String, or specified default value if the parameter does not exist.
145: * This method should only be used if caller is sure that the parameter value is always a sinlge string.
146: * For array of strings getMandatoryParameters() method should be used
147: * Takes whole chain of navs in the consideration */
148: public String getParameter(String pParameterName,
149: String pDefaultValue)
150: throws UIUnexpectedProgramConditionException {
151: String[] lParameters = getParameters(pParameterName,
152: new String[] { pDefaultValue });
153: if (lParameters != null && lParameters.length == 1)
154: return lParameters[0];
155: throw new UIUnexpectedProgramConditionException(
156: "Unexpected value of the optional navigation parameter '"
157: + pParameterName
158: + "'. Expecting single string value.");
159: }
160:
161: /** Returns the value of a navigation parameter as a String, or specified default value if the parameter does not exist.
162: * This method should only be used if caller is sure that the parameter value is always a sinlge string.
163: * For array of strings getMandatoryParameters() method should be used
164: * Only takes this nav in consideration. */
165: public String getLocalParameter(String pParameterName,
166: String pDefaultValue)
167: throws UIUnexpectedProgramConditionException {
168: String[] lParameters = getLocalParameters(pParameterName,
169: new String[] { pDefaultValue });
170: if (lParameters != null && lParameters.length == 1)
171: return lParameters[0];
172: throw new UIUnexpectedProgramConditionException(
173: "Unexpected value of the optional navigation parameter '"
174: + pParameterName
175: + "'. Expecting single string value.");
176: }
177:
178: /** Returns the value of a navigation parameter as a String, or specified default value if the parameter does not exist.
179: * Takes whole chain of navs in the consideration */
180: public String[] getParameters(String pParameterName,
181: String[] pDefaultValue) {
182: for (Application.NavigationMove lMove = this ; lMove != null; lMove = (Application.NavigationMove) (lMove.mFromInstanceId != null ? mNavigationHistory
183: .get(lMove.mFromInstanceId)
184: : null)) {
185: if (lMove.mNavigationParameters
186: .containsKey(pParameterName))
187: return (String[]) lMove.mNavigationParameters
188: .get(pParameterName);
189: }
190: return pDefaultValue;
191: }
192:
193: /** Returns the value of a navigation parameter as a String, or specified default value if the parameter does not exist.
194: * Only takes this nav in consideration. */
195: public String[] getLocalParameters(String pParameterName,
196: String[] pDefaultValue) {
197: if (mNavigationParameters.containsKey(pParameterName))
198: return (String[]) mNavigationParameters
199: .get(pParameterName);
200: return pDefaultValue;
201: }
202:
203: /** Returns ids of all pages for which this navigation has something stored */
204: public String[] getLocalPageIds() {
205: if (mPages == null)
206: return new String[0];
207: return (String[]) mPages.keySet().toArray(
208: new String[mPages.size()]);
209: }
210:
211: /** Returns ids of all forms for which this page has something stored */
212: public String[] getLocalFormIds(String pPageId) {
213: if (mPages == null)
214: return new String[0];
215: PageData lPageData = (PageData) mPages.get(pPageId);
216: if (lPageData == null
217: || lPageData.mFormWidgetModels == null)
218: return new String[0];
219: return (String[]) lPageData.mFormWidgetModels.keySet()
220: .toArray(
221: new String[lPageData.mFormWidgetModels
222: .size()]);
223: }
224:
225: /** Retrieves information about all widget values stored for a page only in this nav
226: * @returns unmodifiable Map with widget resource id / List pairs or null if nothing is stored for the requested page */
227: public Map getLocalWidgetValues(String pPageId) {
228: if (mPages == null)
229: return null;
230: PageData lPageData = (PageData) mPages.get(pPageId);
231: if (lPageData == null || lPageData.mWidgetModels == null)
232: return null;
233: return java.util.Collections
234: .unmodifiableMap(lPageData.mWidgetModels);
235: }
236:
237: /** Retrieves information about all widget values stored for a page only in this nav
238: * @returns unmodifiable Map with widget resource id / List pairs or null if nothing is stored for the requested page */
239: public Map getLocalFormWidgetValues(String pPageId,
240: String pFormId) {
241: if (mPages == null)
242: return null;
243: PageData lPageData = (PageData) mPages.get(pPageId);
244: if (lPageData == null
245: || lPageData.mFormWidgetModels == null)
246: return null;
247: Map lFormWidgetValues = (Map) lPageData.mFormWidgetModels
248: .get(pFormId);
249: if (lFormWidgetValues == null)
250: return null;
251: return java.util.Collections
252: .unmodifiableMap(lFormWidgetValues);
253: }
254:
255: /** Retrieves information about particular widget values obtained from this nav and its predescessors
256: * @return List of widget string values (size 1 for the single value fields) or null if nothing is stored for the widget */
257: public WidgetModel getWidgetModel(String pPageId,
258: String pWidgetResourceId) {
259: if (mPages != null) {
260: PageData lPageData = (PageData) mPages.get(pPageId);
261: if (lPageData != null
262: && lPageData.mWidgetModels != null) {
263: WidgetModel lWidgetModel = (WidgetModel) lPageData.mWidgetModels
264: .get(pWidgetResourceId);
265: if (lWidgetModel != null)
266: return lWidgetModel;
267: }
268: }
269: // Make a move to the previous navigation
270: if (mFromInstanceId != null) {
271: Application.NavigationMove lPreceedingMove = (Application.NavigationMove) mNavigationHistory
272: .get(mFromInstanceId);
273: if (lPreceedingMove != null)
274: return lPreceedingMove.getWidgetModel(pPageId,
275: pWidgetResourceId);
276: }
277: return null; // Nothing found
278: }
279:
280: /** Retrieves information about particular form widget values obtained from this nav and its predescessors
281: * Form Widget Value is looked at first followed by Widget Value
282: * @return List of widget string values (size 1 for the single value fields) or null if nothing is stored for the widget */
283: public WidgetModel getFormWidgetModel(String pPageId,
284: String pFormId, String pWidgetResourceId) {
285: if (mPages != null) {
286: PageData lPageData = (PageData) mPages.get(pPageId);
287: if (lPageData != null) {
288: if (lPageData.mFormWidgetModels != null) {
289: Map lFormWidgetValues = (Map) lPageData.mFormWidgetModels
290: .get(pFormId);
291: if (lFormWidgetValues != null) {
292: WidgetModel lWidgetModel = (WidgetModel) lFormWidgetValues
293: .get(pWidgetResourceId);
294: if (lWidgetModel != null)
295: return lWidgetModel;
296: }
297: }
298: // Now check generic page storage
299: if (lPageData.mWidgetModels != null) {
300: WidgetModel lWidgetModel = (WidgetModel) lPageData.mWidgetModels
301: .get(pWidgetResourceId);
302: if (lWidgetModel != null)
303: return lWidgetModel;
304: }
305: }
306: }
307: // Make a move to the previous navigation
308: if (mFromInstanceId != null) {
309: Application.NavigationMove lPreceedingMove = (Application.NavigationMove) mNavigationHistory
310: .get(mFromInstanceId);
311: if (lPreceedingMove != null)
312: return lPreceedingMove.getFormWidgetModel(pPageId,
313: pFormId, pWidgetResourceId);
314: }
315: return null; // Nothing found
316: }
317:
318: /** Stores Widget value for particular widget on particular page */
319: public void putWidgetModel(String pPageId,
320: String pWidgetResourceId, WidgetModel pWidgetModel) {
321: if (mPages == null)
322: mPages = new HashMap();
323: PageData lPageData = (PageData) mPages.get(pPageId);
324: if (lPageData == null)
325: mPages.put(pPageId, lPageData = new PageData());
326: if (lPageData.mWidgetModels == null)
327: lPageData.mWidgetModels = new HashMap();
328: lPageData.mWidgetModels
329: .put(pWidgetResourceId, pWidgetModel);
330: }
331:
332: /** Stores Widget value for particular widget on particular page */
333: public void putFormWidgetModel(String pPageId, String pFormId,
334: String pWidgetResourceId, WidgetModel pWidgetModel) {
335: if (mPages == null)
336: mPages = new HashMap();
337: PageData lPageData = (PageData) mPages.get(pPageId);
338: if (lPageData == null)
339: mPages.put(pPageId, lPageData = new PageData());
340: if (lPageData.mFormWidgetModels == null)
341: lPageData.mFormWidgetModels = new HashMap();
342: Map lFormWidgetValues = (Map) lPageData.mFormWidgetModels
343: .get(pFormId);
344: if (lFormWidgetValues == null)
345: lPageData.mFormWidgetModels.put(pFormId,
346: lFormWidgetValues = new HashMap());
347: lFormWidgetValues.put(pWidgetResourceId, pWidgetModel);
348: }
349: }
350:
351: // Map of all navigation moves which have happened
352: // Key here is the current navigation instance and the value is
353: // navigation instance this navigation came from. This allows
354: // to walk back in the navigation, which is used to fetch the data
355: private HashMap mNavigationHistory = new HashMap();
356: // Store for all value models for all widgets
357: private NavigationMove mRootNavigationMove = null;
358: // Flag telling us to use or not to use hierarchical datastore
359: private boolean mUseHierarchicalDataStore = false;
360:
361: /** Constructs the application object */
362: public Application() {
363: // Create and register root navigation move for this app
364: // This is the only navigation move which does not have a mFromInstanceId set up (it is null)
365: mRootNavigationMove = new NavigationMove();
366: mNavigationHistory.put(mRootNavigationMove.getInstanceId(),
367: mRootNavigationMove);
368: }
369:
370: /** Sets the flag use or not to use Hierarchical Data Store */
371: public void setUseHierarchicalDataStore(
372: boolean pUseHierarchicalDataStore) {
373: mUseHierarchicalDataStore = pUseHierarchicalDataStore;
374: }
375:
376: /** Adds a single step in navigation history to this application.
377: * @param pFromPageInstanceId has to be null if this is the first page in hierarchy or
378: * already registered instance id
379: * @param pToPageInstanceId has to be never before registered in hierarchy */
380: public NavigationMove createNavigationMove(
381: String pFromPageInstanceId, Map pNavigationParameters) {
382: if (mUseHierarchicalDataStore) {
383: // Validate from page
384: if (pFromPageInstanceId != null
385: && mNavigationHistory
386: .containsKey(pFromPageInstanceId) == false)
387: throw new IllegalArgumentException(
388: "'From Page' Instance Id "
389: + pFromPageInstanceId
390: + " is unknown to this application");
391: // Create new navigation move. If pFromPageInstanceId is null - use root move
392: Application.NavigationMove lNewMove = new Application.NavigationMove();
393: lNewMove.mFromInstanceId = (pFromPageInstanceId != null) ? pFromPageInstanceId
394: : mRootNavigationMove.getInstanceId();
395: if (pNavigationParameters != null)
396: lNewMove.mNavigationParameters
397: .putAll(pNavigationParameters);
398: mNavigationHistory.put(lNewMove.mInstanceId, lNewMove);
399: return lNewMove;
400: } else
401: return mRootNavigationMove;
402: }
403:
404: /** Retrieves the move by it's instance id
405: * @param pNavigationMoveInstanceId
406: * @return existing move instance or null if none found */
407: public NavigationMove getNavigationMove(
408: String pNavigationMoveInstanceId) {
409: return (Application.NavigationMove) mNavigationHistory
410: .get(pNavigationMoveInstanceId);
411: }
412:
413: /** Retrieves the move by it's instance id
414: * @param pThisMove the move we are looking for predescessor for
415: * @return existing move instance or null if given move does not have predescessor */
416: public Application.NavigationMove getPreceedingNavigationMove(
417: Application.NavigationMove pThisMove) {
418: return (Application.NavigationMove) mNavigationHistory
419: .get(pThisMove.mFromInstanceId);
420: }
421:
422: /** @return true if supplied url owned by this application.
423: * For the moment only relative urls (the ones without protocol refs)
424: * are considered to be owned by this application */
425: public boolean isOwnedUrl(String pUrl) {
426: if (pUrl == null)
427: return false;
428: if (Util.isLocalUrl(pUrl))
429: return true;
430: return false;
431: }
432:
433: /** Outputs statistics log */
434: public void logStatistics() {
435: sLogger.warn("Total navs : " + mNavigationHistory.size());
436: sLogger.warn("Total memory : "
437: + Runtime.getRuntime().totalMemory());
438: sLogger.warn("Free memory : "
439: + Runtime.getRuntime().freeMemory());
440: }
441: }
|