View Javadoc

1   /*	
2   	Copyright 2007-2014 Fraunhofer IGD, http://www.igd.fraunhofer.de
3   	Fraunhofer-Gesellschaft - Institute for Computer Graphics Research
4   	
5   	See the NOTICE file distributed with this work for additional 
6   	information regarding copyright ownership
7   	
8   	Licensed under the Apache License, Version 2.0 (the "License");
9   	you may not use this file except in compliance with the License.
10  	You may obtain a copy of the License at
11  	
12  	  http://www.apache.org/licenses/LICENSE-2.0
13  	
14  	Unless required by applicable law or agreed to in writing, software
15  	distributed under the License is distributed on an "AS IS" BASIS,
16  	WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17  	See the License for the specific language governing permissions and
18  	limitations under the License.
19   */
20  package org.universAAL.middleware.ui.rdf;
21  
22  import java.util.List;
23  
24  import javax.xml.datatype.XMLGregorianCalendar;
25  
26  import org.universAAL.middleware.owl.ManagedIndividual;
27  import org.universAAL.middleware.owl.MergedRestriction;
28  import org.universAAL.middleware.rdf.Resource;
29  import org.universAAL.middleware.rdf.TypeMapper;
30  import org.universAAL.middleware.ui.UICaller;
31  import org.universAAL.middleware.ui.owl.DialogType;
32  
33  /**
34   * Forms can be used to describe dialogs in a modality- & layout-neutral way. A
35   * Form is a container of a set of {@link FormControl}s that specify the
36   * structure of the dialog as it should be presented to human users. This
37   * implementation organizes the {@link FormControl}s in three standard
38   * {@link Group}s:
39   * <dl>
40   * <dt>The submits group</dt>
41   * <dd>"buttons" that finish the whole dialog should be added to this group.</dd>
42   * <dt>The ioControls group</dt>
43   * <dd>all other form controls, no matter if input or output controls, or
44   * subgroups or even submits that trigger a sub-dialog should be added to this
45   * group.</dd>
46   * <dt>The stdButton group</dt>
47   * <dd>this group is reserved for a dialog management solution that has access
48   * to all dialogs and may be willing to add standard buttons beyond the
49   * application logic to reflect a system-wide behavior.</dd>
50   * </dl>
51   * Forms support four dialog types:
52   * <dl>
53   * <dt>Message</dt>
54   * <dd>The simplest dialog type that can be used to specify a text message to be
55   * presented as a notification. No standard buttons in the above sense are
56   * allowed. The text message will be used to construct a {@link SimpleOutput}
57   * object that is added as the single child to the ioControls group. The submits
58   * group will be constructed automatically with two buttons based on the
59   * constants {@link #ACK_MESSAGE_DELET} resp. {@link #ACK_MESSAGE_KEEP} and
60   * {@link #LABEL_MESSAGE_DELET} resp. {@link #LABEL_MESSAGE_KEEP}. These two
61   * buttons can be used by a user to state either
62   * "ok, I got it, you can delete the message" or "please preserve the message
63   * for later check", respectively. Normally, applications do not need to
64   * subscribe for user decision in this regard, but if they want, then they must
65   * use the value returned by the method {@link #getDialogID()} of the created
66   * form object, for subscribing to the UI bus.</dd>
67   * <dt>System Menu</dt>
68   * <dd>Reserved for a dialog management solution to present the system main
69   * menu. No submits group is allowed in this type of dialog.</dd>
70   * <dt>Subdialog</dt>
71   * <dd>A dialog related to a running dialog that must be "popped up" when the
72   * user presses a button in the originally running dialog without ending that
73   * running dialog. No standard buttons in the above sense are allowed.
74   * Applications must use instances of {@link SubdialogTrigger} for placing
75   * buttons that pop up a subdialog, instead of instances of {@link Submit} that
76   * should be used only for finishing the whole dialog. {@link Submit} instances
77   * must be added to the group returned by Form.getSubmits(), whereas
78   * {@link SubdialogTrigger} instances should be added to appropriate subgroups
79   * of the group returned by Form.getIOControls(). Subdialogs may affect the data
80   * to be used within the main dialog and hence the main dialog must be frozen
81   * until the application requests to continue with it. For this purpose,
82   * applications must call the method
83   * {@link UICaller#resumeDialog(String,org.universAAL.middleware.rdf.Resource)}
84   * of their output publisher after the subdialog finishes and they have
85   * processed its data and updated the form data of the main dialog. UI handlers
86   * may decide to render instances of {@link SubdialogTrigger} and {@link Submit}
87   * differently. Additionally, they may differentiate between events from these
88   * two types of buttons (and e.g. keep the parent dialog open until the
89   * subdialog loop is closed) or not. In any case, the middleware will
90   * re-dispatch the main dialog by calling
91   * {@link org.universAAL.middleware.ui.UIHandler#handleUICall(org.universAAL.middleware.ui.UIRequest)}
92   * of the output subscriber of the selected UI handler, as soon as the
93   * application requests to resume the dialog. In this way, the freezing and
94   * re-activating the main dialog is forced by the middleware even if the UI
95   * handler does not differentiate between events from instances of
96   * {@link SubdialogTrigger} and {@link Submit}.</dd>
97   * <dt>Standard Dialog</dt>
98   * <dd>All other forms must be constructed with this dialog type. All the three
99   * standard groups will be created automatically. Applications can get the
100  * standard groups "ioControls" and "submits" by calling the
101  * {@link #getIOControls()} and {@link #getSubmits()} methods respectively and
102  * construct their dialogs using these two groups as parent for their real form
103  * controls.</dd>
104  * </dl>
105  * 
106  * @see <a
107  *      href="ftp://ftp.igd.fraunhofer.de/outgoing/mtazari/persona/dialogPackage.jpg">
108  *      ftp://ftp.igd.fraunhofer.de/outgoing/mtazari/persona/dialogPackage.jpg</a>
109  * @author mtazari
110  * @author Carsten Stockloew
111  * @navassoc - "IOControlls\n Submits\n StandardButtons" - Group
112  * @navassoc - "parentDialog" 1 Form
113  */
114 public class Form extends FormElement {
115     public static final String uAAL_DIALOG_NAMESPACE = uAAL_NAMESPACE_PREFIX
116 	    + "Dialog.owl#";
117     public static final String MY_URI = uAAL_DIALOG_NAMESPACE + "Form";
118 
119     /**
120      * The submission ID if a user acknowledges that a dialog of type Message
121      * can be deleted.
122      */
123     public static final String ACK_MESSAGE_DELET = "deleteMessage";
124 
125     /**
126      * The submission ID if a user acknowledges that a dialog of type Message
127      * must be preserved for later check.
128      */
129     public static final String ACK_MESSAGE_KEEP = "preserveMessage";
130 
131     /**
132      * The label of a button associated with {@link #ACK_MESSAGE_DELET} as
133      * submission ID that is automatically added to dialogs of type Message.
134      */
135     public static final Label LABEL_MESSAGE_DELET = new Label("Delete", null);
136 
137     /**
138      * The label of a button associated with {@link #ACK_MESSAGE_KEEP} as
139      * submission ID that is automatically added to dialogs of type Message.
140      */
141     public static final Label LABEL_MESSAGE_KEEP = new Label("Preserve", null);
142 
143     /**
144      * An optional property of form objects that can be associated with a string
145      * representing the name of the component that created the form.
146      */
147     public static final String PROP_DIALOG_CREATED_BY = uAAL_DIALOG_NAMESPACE
148 	    + "createdBy";
149 
150     /**
151      * The point of time in which a form object is instantiated. This property
152      * is set automatically with the value returned by
153      * {@link org.universAAL.middleware.rdf.TypeMapper#getCurrentDateTime()}.
154      */
155     public static final String PROP_DIALOG_CREATION_TIME = uAAL_DIALOG_NAMESPACE
156 	    + "creationTimestamp";
157 
158     /**
159      * An optional property of form objects to indicate which form control
160      * should receive the focus when UI handlers start to present the dialog. It
161      * will be set automatically by the middleware whenever a running dialog is
162      * cut so that a seamless resumption of the dialog at a later point in time
163      * is guaranteed. UI handlers should check if this property is set. If yes,
164      * then they must simulate their logic of presenting the form until they
165      * reach the form control given as value of this property. At this point
166      * they can prompt the user for the next data entry.
167      */
168     public static final String PROP_DIALOG_CURRENT_FOCUSED_CONTROL = uAAL_DIALOG_NAMESPACE
169 	    + "currentFocus";
170 
171     /**
172      * The {@link org.universAAL.middleware.rdf.Resource} containing the form
173      * data. Form data can be accessed using property paths; UI handlers,
174      * however, do not need to explicitly access this data normally, because
175      * they normally deal only with data associated with form controls that can
176      * be retrieved by calling {@link FormControl#getValue()} or set by calling
177      * {@link Input#storeUserInput(Object)}. Applications may create an instance
178      * of {@link org.universAAL.middleware.rdf.Resource} and set both their
179      * hidden data and initial data associated with the form controls using
180      * {@link org.universAAL.middleware.rdf.Resource#setPropertyPath(String[], Object)}
181      * . They can retrieve the form data from ui responses by calling
182      * {@link org.universAAL.middleware.ui.UIResponse#getUserInput(String[])} .
183      * Note: initial data to be associated with form controls can be set through
184      * their constructors, as well.
185      */
186     public static final String PROP_DIALOG_DATA_ROOT = uAAL_DIALOG_NAMESPACE
187 	    + "dialogDataRoot";
188 
189     /**
190      * The type of the dialog represented by a form object as an instance of
191      * {@link DialogType}. See the above documentation of this class regarding
192      * the types of dialogs supported.
193      */
194     public static final String PROP_DIALOG_TYPE = uAAL_DIALOG_NAMESPACE
195 	    + "dialogType";
196 
197     /**
198      * Applications must set this property for those form objects representing a
199      * dialog of type Subdialog using the value returned by
200      * {@link #getDialogID()} on the parent form from which the subdialog was
201      * triggered.
202      */
203     public static final String PROP_PARENT_DIALOG_URI = uAAL_DIALOG_NAMESPACE
204 	    + "parentDialogURI";
205 
206     protected static final String PROP_ROOT_GROUP = uAAL_DIALOG_NAMESPACE
207 	    + "rootGroup";
208 
209     private static final String STD_BUTTONS_DIALOG_ID_SUFFIX = "stdButtons";
210 
211     static MergedRestriction getPPathRestriction(String[] pp, Resource pr) {
212 	if (pp == null || pp.length == 0 || pr == null)
213 	    return null;
214 	for (int i = 0; i < pp.length - 1; i++) {
215 	    Object o = pr.getProperty(pp[i]);
216 	    if (o == null)
217 		return getPPathRestriction(pp, i, pr.getType());
218 	    else if (o instanceof Resource)
219 		pr = (Resource) o;
220 	    else
221 		return null;
222 	}
223 	return ManagedIndividual.getClassRestrictionsOnProperty(pr.getType(),
224 		pp[pp.length - 1]);
225     }
226 
227     static MergedRestriction getPPathRestriction(String[] pp, int i,
228 	    String typeURI) {
229 	if (typeURI == null)
230 	    return null;
231 
232 	MergedRestriction r = ManagedIndividual.getClassRestrictionsOnProperty(
233 		typeURI, pp[i]);
234 	if (i == pp.length - 1)
235 	    return r;
236 
237 	return (r == null) ? null : getPPathRestriction(pp, i + 1, r
238 		.getPropTypeURI());
239     }
240 
241     static Object getValue(String[] pp, Resource pr) {
242 	if (pp == null || pp.length == 0 || pr == null)
243 	    return null;
244 
245 	Object o = pr.getProperty(pp[0]);
246 	for (int i = 1; o != null && i < pp.length; i++) {
247 	    if (!(o instanceof Resource))
248 		return null;
249 	    pr = (Resource) o;
250 	    o = pr.getProperty(pp[i]);
251 	}
252 
253 	return o;
254     }
255 
256     /**
257      * Constructs and returns a new form object representing an empty dialog of
258      * type {@link DialogType#stdDialog Standard Dialog} with proper initial
259      * configuration. See also the discussion of dialog types in the above
260      * documentation of this class.
261      * 
262      * @param formTitle
263      *            The form title giving the intent of the dialog.
264      * @param dataRoot
265      *            The Resource containing the form data. It can be null, if no
266      *            hidden data was prepared by the application and any initial
267      *            value to be associated with form controls is going to be set
268      *            through their constructors. See also
269      *            {@link #PROP_DIALOG_DATA_ROOT}.
270      * @return A newly constructed form object representing an empty dialog of
271      *         type {@link DialogType#stdDialog Standard Dialog} with proper
272      *         initial configuration.
273      */
274     public static Form newDialog(String formTitle, Resource dataRoot) {
275 	Form f = new Form(formTitle, dataRoot);
276 	f.props.put(PROP_DIALOG_TYPE, DialogType.stdDialog);
277 	Group root = (Group) f.props.get(PROP_ROOT_GROUP);
278 	new Group(root, new Label(Group.STD_IO_CONTROLS, null), null, null,
279 		null);
280 	new Group(root, new Label(Group.STD_SUBMITS, null), null, null, null);
281 	new Group(root, new Label(Group.STD_STD_BUTTONS, null), null, null,
282 		null);
283 	return f;
284     }
285 
286     /**
287      * An alternative for {@link #newDialog(String, Resource)} to be used if no
288      * specific data root is going to be specified but only the URI of its type.
289      */
290     public static Form newDialog(String formTitle, String dataRootType) {
291 	Form f = new Form(formTitle, dataRootType);
292 	f.props.put(PROP_DIALOG_TYPE, DialogType.stdDialog);
293 	Group root = (Group) f.props.get(PROP_ROOT_GROUP);
294 	new Group(root, new Label(Group.STD_IO_CONTROLS, null), null, null,
295 		null);
296 	new Group(root, new Label(Group.STD_SUBMITS, null), null, null, null);
297 	new Group(root, new Label(Group.STD_STD_BUTTONS, null), null, null,
298 		null);
299 	return f;
300     }
301 
302     /**
303      * Constructs and returns a new form object representing a
304      * {@link DialogType#message Message} dialog that is ready to publish within
305      * an {@link org.universAAL.middleware.ui.UIRequest}. See also the
306      * discussion of dialog types in the above documentation of this class.
307      * 
308      * @param formTitle
309      *            The form title giving the intent of the dialog.
310      * @param message
311      *            The text of the message.
312      * @return A newly constructed form object representing a
313      *         {@link DialogType#message Message} dialog that is ready to
314      *         publish within an {@link org.universAAL.middleware.ui.UIRequest}.
315      */
316     public static Form newMessage(String formTitle, String message) {
317 	Form f = new Form(formTitle, (Resource) null);
318 	f.props.put(PROP_DIALOG_TYPE, DialogType.message);
319 	Group root = (Group) f.props.get(PROP_ROOT_GROUP);
320 	Group ctrls = new Group(root, new Label(Group.STD_IO_CONTROLS, null),
321 		null, null, null);
322 	new SimpleOutput(ctrls, null, null, message);
323 	Group submits = new Group(root, new Label(Group.STD_SUBMITS, null),
324 		null, null, null);
325 	new Submit(submits, LABEL_MESSAGE_DELET, ACK_MESSAGE_DELET);
326 	new Submit(submits, LABEL_MESSAGE_KEEP, ACK_MESSAGE_KEEP);
327 	return f;
328     }
329 
330     /**
331      * Constructs and returns a new form object representing an empty dialog of
332      * type {@link DialogType#subdialog Subdialog} with proper initial
333      * configuration. See also the discussion of dialog types in the above
334      * documentation of this class. To set hidden form data, you must first get
335      * the data root using {@link #getData()} and then add your data calling its
336      * method
337      * {@link org.universAAL.middleware.rdf.Resource#setPropertyPath(String[], Object)}
338      * .
339      * 
340      * @param formTitle
341      *            The form title giving the intent of the dialog.
342      * @param parentDialogURI
343      *            The ID of the parent dialog. See also
344      *            {@link #PROP_PARENT_DIALOG_URI}.
345      * @return A newly constructed form object representing an empty dialog of
346      *         type {@link DialogType#subdialog Subdialog} with proper initial
347      *         configuration.
348      */
349     public static Form newSubdialog(String formTitle, String parentDialogURI) {
350 	Form f = new Form(formTitle, (Resource) null);
351 	f.props.put(PROP_DIALOG_TYPE, DialogType.subdialog);
352 	f.props.put(PROP_PARENT_DIALOG_URI, new Resource(parentDialogURI));
353 	Group root = (Group) f.props.get(PROP_ROOT_GROUP);
354 	new Group(root, new Label(Group.STD_IO_CONTROLS, null), null, null,
355 		null);
356 	new Group(root, new Label(Group.STD_SUBMITS, null), null, null, null);
357 	return f;
358     }
359 
360     public static Form newSystemMenu(String formTitle) {
361 	Form f = new Form(formTitle, (Resource) null);
362 	f.props.put(PROP_DIALOG_TYPE, DialogType.sysMenu);
363 	Group root = (Group) f.props.get(PROP_ROOT_GROUP);
364 	new Group(root, new Label(Group.STD_IO_CONTROLS, null), null, null,
365 		null);
366 	new Group(root, new Label(Group.STD_STD_BUTTONS, null), null, null,
367 		null);
368 	return f;
369     }
370 
371     static boolean setValue(Resource pr, String[] pp, Object value,
372 	    MergedRestriction valueRestrictions) {
373 	if (pp == null || pp.length == 0)
374 	    return false;
375 
376 	if (value instanceof List && ((List) value).isEmpty())
377 	    value = null;
378 	else if (value != null && valueRestrictions != null) {
379 	    Resource dummy = new Resource();
380 	    dummy.setProperty(pp[pp.length - 1], value);
381 	    if (!valueRestrictions.hasMember(dummy))
382 		return false;
383 	}
384 
385 	return pr.setPropertyPathFromOffset(pp, 0, value, true);
386     }
387 
388     /**
389      * For usage by de-serializers only.
390      */
391     public Form(String uri) {
392 	super(uri);
393 	addType(MY_URI, true);
394     }
395 
396 	/**
397 	 * @param uriPrefix
398 	 * @param numProps
399 	 */
400 	protected Form(String uriPrefix, int numProps) {
401 		super(uriPrefix, numProps);
402 	}
403     
404     private Form(String formTitle, Resource dataRoot) {
405 	super(uAAL_DIALOG_NAMESPACE, 5);
406 	addType(MY_URI, true);
407 	props.put(PROP_DIALOG_CREATION_TIME, TypeMapper.getCurrentDateTime());
408 	props.put(PROP_ROOT_GROUP, new Group(formTitle, this));
409 	props.put(PROP_DIALOG_DATA_ROOT, (dataRoot == null) ? new Resource()
410 		: dataRoot);
411     }
412 
413     private Form(String formTitle, String dataRootType) {
414 	super(uAAL_DIALOG_NAMESPACE, 5);
415 	addType(MY_URI, true);
416 	props.put(PROP_DIALOG_CREATION_TIME, TypeMapper.getCurrentDateTime());
417 	props.put(PROP_ROOT_GROUP, new Group(formTitle, this));
418 	Resource root = ManagedIndividual.getInstance(dataRootType, null);
419 	if (root == null)
420 	    root = new Resource();
421 	props.put(PROP_DIALOG_DATA_ROOT, root);
422     }
423 
424     void finalizeGroupStructure() {
425 	FormControl[] children = getRootGroup().getChildren();
426 	if (children != null)
427 	    for (int i = 0; i < children.length; i++)
428 		if (children[i] instanceof Group)
429 		    ((Group) children[i]).setStructuralProps(0);
430     }
431 
432     /**
433      * Returns the time at which the first time the form was created by an
434      * application.
435      * 
436      * @see #PROP_DIALOG_CREATION_TIME
437      */
438     public XMLGregorianCalendar getCreationTime() {
439 	return (XMLGregorianCalendar) props.get(PROP_DIALOG_CREATION_TIME);
440     }
441 
442     /**
443      * @see #PROP_DIALOG_CURRENT_FOCUSED_CONTROL
444      */
445     public FormControl getCurrentFocusedControl() {
446 	return (FormControl) props.get(PROP_DIALOG_CURRENT_FOCUSED_CONTROL);
447     }
448 
449     /**
450      * @see #PROP_DIALOG_DATA_ROOT
451      */
452     public Resource getData() {
453 	return (Resource) props.get(PROP_DIALOG_DATA_ROOT);
454     }
455 
456     /**
457      * @see #PROP_DIALOG_CREATED_BY
458      */
459     public String getDialogCreator() {
460 	return (String) props.get(PROP_DIALOG_CREATED_BY);
461     }
462 
463     /**
464      * Returns the URI of this form object as its global unique ID.
465      */
466     public String getDialogID() {
467 	return uri;
468     }
469 
470     /**
471      * @see #PROP_DIALOG_TYPE
472      */
473     public DialogType getDialogType() {
474 	return (DialogType) props.get(PROP_DIALOG_TYPE);
475     }
476 
477     // private int getIndex(String index) {
478     // try { return Integer.parseInt(index); } catch (Exception e) { return -1;
479     // }
480     // }
481 
482     /**
483      * Returns the standard group for UI controls in this form. See also the
484      * above documentation of this class concerning the standard groups within
485      * forms.
486      */
487     public Group getIOControls() {
488 	FormControl[] children = getRootGroup().getChildren();
489 	for (int i = 0; i < children.length; i++)
490 	    if (children[i] instanceof Group
491 		    && Group.STD_IO_CONTROLS.equals(children[i].getLabel()
492 			    .getText()))
493 		return (Group) children[i];
494 	return null;
495     }
496 
497     /**
498      * Returns the text message originally set if this form was created by
499      * {@link #newMessage(String, String)}. Otherwise, it returns null.
500      */
501     public String getMessageContent() {
502 	if (isMessage()) {
503 	    Group main = getIOControls();
504 	    try {
505 		return (String) ((SimpleOutput) main.getChildren()[0])
506 			.getContent();
507 	    } catch (Exception e) {
508 	    }
509 	}
510 	return null;
511     }
512 
513     /**
514      * Returns the parent dialog as an empty resource with
515      * {@link #PROP_PARENT_DIALOG_URI} as its URI.
516      */
517     public Resource getParentDialogResource() {
518 	return isSubdialog() ? (Resource) props.get(PROP_PARENT_DIALOG_URI)
519 		: null;
520     }
521 
522     /**
523      * @see #PROP_PARENT_DIALOG_URI
524      */
525     public String getParentDialogURI() {
526 	return isSubdialog() ? props.get(PROP_PARENT_DIALOG_URI).toString()
527 		: null;
528     }
529 
530     MergedRestriction getPPathRestriction(String[] pp) {
531 	return getPPathRestriction(pp, getData());
532     }
533 
534     Group getRootGroup() {
535 	return (Group) props.get(PROP_ROOT_GROUP);
536     }
537 
538     /**
539      * Returns all the {@link Output} controls contained in the ioControls group
540      * (see the above documentation of this class concerning the standard groups
541      * within forms) that are likely to be relevant for human users in order to
542      * decide what to do with this dialog. The assumption is that there may be
543      * other {@link Output} controls specific to certain submissions possible
544      * within this dialog, which will be ignored by this method as they do not
545      * represent shared info.
546      */
547     public Output[] getSharedOutputs() {
548 	return getIOControls().getFirstLevelOutputs();
549     }
550 
551     /**
552      * Returns the pre-defined group of standard buttons in this form. See also
553      * the above documentation of this class concerning the standard groups
554      * within forms.
555      */
556     public Group getStandardButtons() {
557 	FormControl[] children = getRootGroup().getChildren();
558 	for (int i = 0; i < children.length; i++)
559 	    if (children[i] instanceof Group
560 		    && Group.STD_STD_BUTTONS.equals(children[i].getLabel()
561 			    .getText()))
562 		return (Group) children[i];
563 	return null;
564     }
565 
566     /**
567      * Reserved for use by a dialog management solution that has access to all
568      * dialogs and adds standard buttons beyond the application logic to reflect
569      * a system-wide behavior.
570      */
571     public String getStandardButtonsDialogID() {
572 	return uri + STD_BUTTONS_DIALOG_ID_SUFFIX;
573     }
574 
575     /**
576      * Returns the standard group for submit buttons in this form. See also the
577      * above documentation of this class concerning the standard groups within
578      * forms.
579      */
580     public Group getSubmits() {
581 	FormControl[] children = getRootGroup().getChildren();
582 	for (int i = 0; i < children.length; i++)
583 	    if (children[i] instanceof Group
584 		    && Group.STD_SUBMITS.equals(children[i].getLabel()
585 			    .getText()))
586 		return (Group) children[i];
587 	return null;
588     }
589 
590     /**
591      * Returns the form title reflecting the intent of this dialog and
592      * originally set when constructing this form object.
593      */
594     public String getTitle() {
595 	try {
596 	    return getRootGroup().getLabel().getText();
597 	} catch (NullPointerException npe) {
598 	    return null;
599 	}
600     }
601 
602     String getTypeURI(String[] pp) {
603 	MergedRestriction r = getPPathRestriction(pp);
604 	return (r == null) ? null : r.getPropTypeURI();
605     }
606 
607     Object getValue(String[] pp) {
608 	return getValue(pp, getData());
609     }
610 
611     /**
612      * Answers if this form object is created by
613      * {@link #newDialog(String, Resource)}.
614      * 
615      * @see #PROP_DIALOG_TYPE
616      */
617     public boolean isStandardDialog() {
618 	return DialogType.stdDialog.equals(props.get(PROP_DIALOG_TYPE));
619     }
620 
621     /**
622      * Answers if this form object is created by
623      * {@link #newMessage(String, String)}.
624      * 
625      * @see #PROP_DIALOG_TYPE
626      */
627     public boolean isMessage() {
628 	return DialogType.message.equals(props.get(PROP_DIALOG_TYPE));
629     }
630 
631     /**
632      * Answers if this form object is created by
633      * {@link #newSubdialog(String, String)}.
634      * 
635      * @see #PROP_DIALOG_TYPE
636      */
637     public boolean isSubdialog() {
638 	return DialogType.subdialog.equals(props.get(PROP_DIALOG_TYPE));
639     }
640 
641     /**
642      * Answers if this form object is created by {@link #newSystemMenu(String)}.
643      * 
644      * @see #PROP_DIALOG_TYPE
645      */
646     public boolean isSystemMenu() {
647 	return DialogType.sysMenu.equals(props.get(PROP_DIALOG_TYPE));
648     }
649 
650     /**
651      * @see #PROP_DIALOG_CURRENT_FOCUSED_CONTROL
652      */
653     public void setCurrentFocusedControl(FormControl fc) {
654 	if (fc != null)
655 	    props.put(PROP_DIALOG_CURRENT_FOCUSED_CONTROL, fc);
656 	else
657 	    props.remove(PROP_DIALOG_CURRENT_FOCUSED_CONTROL);
658     }
659 
660     /**
661      * @see #PROP_DIALOG_CREATED_BY
662      */
663     public void setDialogCreator(String creator) {
664 	if (creator != null)
665 	    props.put(PROP_DIALOG_CREATED_BY, creator);
666     }
667 
668     boolean setValue(String[] pp, Object value,
669 	    MergedRestriction valueRestrictions) {
670 	return setValue(getData(), pp, value, valueRestrictions);
671     }
672 
673     /**
674      * Reserved for usage by the middleware.
675      */
676     public void substituteData(Resource pr) {
677 	Resource cur = getData();
678 	if (pr != null && pr != cur) {
679 	    String t0 = cur.getType(), t1 = pr.getType();
680 	    if (t0 == t1 || (t0 != null && t0.equals(t1)))
681 		props.put(PROP_DIALOG_DATA_ROOT, pr);
682 	}
683     }
684     
685     /**
686      * look for a FormControl within the form with the given URI.
687      * @param formControlURI
688      * @return the {@link FormControl} or null if not found.
689      */
690     public FormControl searchFormControl(String formControlURI){
691     	FormControl[] children = getRootGroup().getChildren();
692     	boolean found = false;
693     	int i = 0;
694     	FormControl result = null;
695     	while (!found
696     			&& i < children.length){
697     		found = children[i].getURI().equals(formControlURI);
698     		if (found){
699     			result = children[i];
700     		}
701     		else if (children[i] instanceof Group){
702     			result = ((Group)children[i]).searchFormControl(formControlURI);
703     			found = (result != null);
704     		}
705     		i++;
706     	}
707     	return result;
708     }
709 }