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.Iterator;
23  import java.util.LinkedList;
24  import java.util.List;
25  
26  import org.universAAL.middleware.owl.MergedRestriction;
27  import org.universAAL.middleware.rdf.PropertyPath;
28  import org.universAAL.middleware.rdf.TypeMapper;
29  
30  /**
31   * The structural unit of forms that may bear information to be presented to
32   * human users and / or serve as a placeholder for user input.
33   * 
34   * @see <a
35   *      href="ftp://ftp.igd.fraunhofer.de/outgoing/mtazari/persona/dialogPackage.jpg">
36   *      ftp://ftp.igd.fraunhofer.de/outgoing/mtazari/persona/dialogPackage.jpg</a>
37   * @author mtazari
38   * @author Carsten Stockloew
39   * @navassoc - "label" 1 Label
40   * @navassoc - "parentGroup" 1 Group
41   */
42  public abstract class FormControl extends FormElement {
43  
44      /**
45       * Form controls may have a {@link Label}.
46       */
47      public static final String PROP_CONTROL_LABEL = Form.uAAL_DIALOG_NAMESPACE
48  	    + "controlLabel";
49  
50      /**
51       * Form controls may have a help string to be presented to human users when
52       * they need more info about the role of a form control.
53       */
54      public static final String PROP_HELP = Form.uAAL_DIALOG_NAMESPACE
55  	    + "ctrlHelp";
56  
57      /**
58       * Form controls may have a hint string as a short hint about the role of a
59       * form control. Confer the concept of tool-tips in graphical user
60       * interfaces.
61       */
62      public static final String PROP_HINT = Form.uAAL_DIALOG_NAMESPACE
63  	    + "ctrlHint";
64  
65      /**
66       * Apart from the three standard groups described in {@link Form}, all other
67       * form controls are contained in a {@link Group}.
68       */
69      public static final String PROP_PARENT_CONTROL = Form.uAAL_DIALOG_NAMESPACE
70  	    + "parentControl";
71  
72      /**
73       * If a form control is allowed to have associated data (initial value or
74       * user input) it must have a property path that is used to access related
75       * data within {@link Form#PROP_DIALOG_DATA_ROOT}.
76       */
77      public static final String PROP_REFERENCED_PPATH = Form.uAAL_DIALOG_NAMESPACE
78  	    + "controlRef";
79  
80      /**
81       * Can be used to define local restrictions on the value of form controls in
82       * addition to restrictions possibly derivable from possibly available form
83       * data (the latter are called model-based restrictions).
84       */
85      public static final String PROP_VALUE_RESTRICTION = Form.uAAL_DIALOG_NAMESPACE
86  	    + "valueRestrictions";
87  
88      protected FormControl() {
89  	super();
90      }
91  
92      protected FormControl(String typeURI, Object parent, Label label,
93  	    PropertyPath ref, MergedRestriction valueRestriction,
94  	    Object initialValue) {
95  	super();
96  	addType(typeURI, true);
97  	props.put(PROP_PARENT_CONTROL, parent);
98  	if (parent instanceof Group)
99  	    ((Group) parent).addChild(this);
100 	else if (!(this instanceof Group) || !(parent instanceof Form)
101 		|| ((Form) parent).getRootGroup() != null)
102 	    throw new IllegalArgumentException(
103 		    "Parent now allowed for this control!");
104 	if (label != null)
105 	    props.put(PROP_CONTROL_LABEL, label);
106 	if (parent instanceof Repeat)
107 	    return;
108 	if (ref != null) {
109 	    props.put(PROP_REFERENCED_PPATH, ref);
110 	    if (valueRestriction != null)
111 		props.put(PROP_VALUE_RESTRICTION, valueRestriction);
112 	    if (initialValue != null) {
113 		Form f = getFormObject();
114 		if (f == null
115 			|| !f.setValue(ref.getThePath(), initialValue,
116 				valueRestriction))
117 		    throw new IllegalArgumentException(
118 			    "Initial value doesn not match the restrictions!");
119 	    }
120 	} else if (initialValue != null || valueRestriction != null)
121 	    throw new IllegalArgumentException(
122 		    "Cannot store an initial value or a value restriction without any reference path!");
123     }
124 
125     /**
126      * Returns the nearest {@link Repeat} control containing this control if
127      * any. Identical with {@link #getParentRepeat()} if this control is a
128      * column in a {@link Repeat} control.
129      */
130     public Repeat getAncestorRepeat() {
131 	Group g = getParentGroup();
132 	while (g != null && !(g instanceof Repeat))
133 	    g = g.getParentGroup();
134 	return (Repeat) g;
135     }
136 
137     /**
138      * Returns the form object containing this form control.
139      */
140     public Form getFormObject() {
141 	Object cur = this;
142 	while (cur instanceof FormControl)
143 	    cur = ((FormControl) cur).props.get(PROP_PARENT_CONTROL);
144 	if (cur instanceof Form)
145 	    return (Form) cur;
146 	return null;
147     }
148 
149     /**
150      * Returns the help text for this control.
151      * 
152      * @see #PROP_HELP
153      */
154     public String getHelpString() {
155 	return (String) props.get(PROP_HELP);
156     }
157 
158     /**
159      * Returns the hint string for this control.
160      * 
161      * @see #PROP_HINT
162      */
163     public String getHintString() {
164 	return (String) props.get(PROP_HINT);
165     }
166 
167     /**
168      * Returns the {@link Label} of this control.
169      * 
170      * @see #PROP_CONTROL_LABEL
171      */
172     public Label getLabel() {
173 	return (Label) props.get(PROP_CONTROL_LABEL);
174     }
175 
176     MergedRestriction getLocalRestrictions() {
177 	return (MergedRestriction) props.get(PROP_VALUE_RESTRICTION);
178     }
179 
180     /**
181      * Tries to find out the largest number of characters in the string
182      * representation of the values associated with this control. The string
183      * representation is determined simply by calling
184      * {@link java.lang.Object#toString()} on each value. The values are
185      * determined the following way:
186      * <ul>
187      * <li>if value restrictions can be drawn based on available form data, then
188      * possible upper enumeration (see
189      * {@link org.universAAL.middleware.owl.TypeExpression#getUpperEnumeration()
190      * ()} is used
191      * <li>if this control is a direct part of a {@link Repeat} object (i.e.,
192      * assuming that a {@link Repeat} object can be rendered as a table, then a
193      * direct part of it would be rendered as a column of the table), all values
194      * in that column are drawn.
195      * <li>as the last possibility, the value(s) possibly returned by
196      * {@link #getValue()} is used.
197      * </ul>
198      * If no value can be found, -1 is returned.
199      */
200     public int getMaxLength() {
201 	int res = -1;
202 	MergedRestriction r = getRestrictions();
203 	Object[] value = (r == null) ? null : r.getEnumeratedValues();
204 	if (value != null) {
205 	    for (int i = value.length - 1; i > -1; i--) {
206 		if (value[i] != null) {
207 		    int aux = value[i].toString().length();
208 		    if (aux > res)
209 			res = aux;
210 		}
211 	    }
212 	    if (res > -1)
213 		return res;
214 	}
215 
216 	Repeat rp = getParentRepeat();
217 	if (rp != null) {
218 	    List values = rp.getAllValues(getReferencedPPath());
219 	    if (values != null) {
220 		for (Iterator i = values.iterator(); i.hasNext();) {
221 		    Object o = i.next();
222 		    if (o != null) {
223 			int aux = o.toString().length();
224 			if (aux > res)
225 			    res = aux;
226 		    }
227 		}
228 	    }
229 	    if (res > -1)
230 		return res;
231 	}
232 
233 	Object o = getValue();
234 	if (o instanceof List) {
235 	    for (Iterator i = ((List) o).iterator(); i.hasNext();) {
236 		o = i.next();
237 		if (o != null) {
238 		    int aux = o.toString().length();
239 		    if (aux > res)
240 			res = aux;
241 		}
242 	    }
243 	    return res;
244 	}
245 
246 	return (o == null) ? -1 : getValue().toString().length();
247     }
248 
249     /**
250      * Returns possible value restrictions by merging any local or model-based
251      * (defined over available form data) restrictions. Returns null, if both
252      * types of restrictions were null.
253      */
254     public MergedRestriction getRestrictions() {
255 	PropertyPath pp = getReferencedPPath();
256 	MergedRestriction r1 = getLocalRestrictions(), r2 = getParentGroup()
257 		.getPPathRestriction((pp == null ? null : pp.getThePath()));
258 	return (r1 == null) ? r2 : r1.merge(r2);
259     }
260 
261     /**
262      * Returns the Group control that contains this form control as a direct
263      * child.
264      */
265     public Group getParentGroup() {
266 	Object o = props.get(PROP_PARENT_CONTROL);
267 	if (o instanceof Group)
268 	    return (Group) o;
269 	return null;
270     }
271 
272     /**
273      * If this control is a column in a {@link Repeat} control, that
274      * {@link Repeat} control is returned, otherwise null.
275      */
276     public Repeat getParentRepeat() {
277 	Group g = getParentGroup();
278 	if (g != null && !(g instanceof Repeat))
279 	    g = g.getParentGroup();
280 	return (g instanceof Repeat) ? (Repeat) g : null;
281     }
282 
283     /**
284      * @see #PROP_REFERENCED_PPATH
285      */
286     public PropertyPath getReferencedPPath() {
287 	return (PropertyPath) props.get(PROP_REFERENCED_PPATH);
288     }
289 
290     /**
291      * Returns the hierarchy of {@link Group}s containing this form control. The
292      * first element in the returned array (index 0) will be one of the three
293      * standard groups of the form containing this control (see the
294      * documentation of class {@link Form}) and the last element will be its
295      * direct parent group.
296      */
297     public Group[] getSuperGroups() {
298 	LinkedList gl = new LinkedList();
299 	for (Group fc = getParentGroup(); fc != null; fc = fc.getParentGroup()) {
300 	    gl.addFirst(fc);
301 	}
302 	// remove last added since it is the place holder for root groups
303 	gl.removeFirst();
304 	return (Group[]) gl.toArray(new Group[] {});
305     }
306 
307     /**
308      * Returns the URI of the type of values that are / can be associated with
309      * this control if it is decidable, null otherwise.
310      */
311     public String getTypeURI() {
312 	MergedRestriction r = getLocalRestrictions();
313 	if (r != null) {
314 	    String result = r.getPropTypeURI();
315 	    if (result != null)
316 		return result;
317 	}
318 
319 	Group g = getParentGroup();
320 	if (g != null) {
321 	    PropertyPath pp = getReferencedPPath();
322 	    return g.getTypeURI((pp == null) ? null : pp.getThePath());
323 	} else
324 	    return null;
325     }
326 
327     /**
328      * Returns the value(s) currently associated with this control. If this
329      * control represents a column in a {@link Repeat} control, the returned
330      * value will be taken from the currently selected row in the {@link Repeat}
331      * control.
332      */
333     public Object getValue() {
334 	Group g = getParentGroup();
335 	if (g == null)
336 	    return null;
337 
338 	PropertyPath pp = getReferencedPPath();
339 	return g.getValue((pp == null ? null : pp.getThePath()));
340     }
341 
342     /**
343      * Checks if {@link Form#PROP_DIALOG_CURRENT_FOCUSED_CONTROL} has this
344      * control as value.
345      */
346     public boolean hasFocus() {
347 	return getFormObject().getCurrentFocusedControl() == this;
348     }
349 
350     /**
351      * Checks if there is any value associated with this control in the form
352      * data.
353      */
354     public boolean hasValue() {
355 	Group g = getParentGroup();
356 	if (g == null)
357 	    return false;
358 
359 	PropertyPath pp = getReferencedPPath();
360 	return g.hasValue((pp == null ? null : pp.getThePath()));
361     }
362 
363     /**
364      * Checks if the value returned by {@link #getTypeURI()} is equal to
365      * xsd:boolean.
366      */
367     public boolean isOfBooleanType() {
368 	return TypeMapper.getJavaClass(getTypeURI()) == Boolean.class;
369     }
370 
371     /**
372      * Checks if the value returned by {@link #getTypeURI()} is one of those
373      * supported by the {@link org.universAAL.middleware.rdf.TypeMapper}.
374      */
375     public boolean isOfPrimitiveType() {
376 	return TypeMapper.isRegisteredDatatypeURI(getTypeURI());
377     }
378 
379     /**
380      * Checks if this control is a column in a {@link Repeat} control.
381      */
382     public boolean isRepeatable() {
383 	Group g = getParentGroup();
384 	if (g == null)
385 	    return false;
386 	if (g instanceof Repeat)
387 	    return true;
388 
389 	return (g.getParentGroup() instanceof Repeat);
390     }
391 
392     /**
393      * @see #PROP_HELP
394      */
395     public void setHelpString(String value) {
396 	if (value != null && !props.containsKey(PROP_HELP))
397 	    props.put(PROP_HELP, value);
398     }
399 
400     /**
401      * @see #PROP_HINT
402      */
403     public void setHintString(String value) {
404 	if (value != null && !props.containsKey(PROP_HINT))
405 	    props.put(PROP_HINT, value);
406     }
407 
408     /**
409      * For usage by de-serializers.
410      * 
411      * @see org.universAAL.middleware.rdf.Resource#setProperty(String, Object)
412      */
413     public boolean setProperty(String propURI, Object value) {
414 	if (propURI == null || value == null || props.containsKey(propURI))
415 	    return false;
416 
417 	boolean knownProp = false;
418 
419 	if (propURI.equals(PROP_CONTROL_LABEL)) {
420 	    if (value instanceof Label)
421 		knownProp = true;
422 	    else
423 		return false;
424 	} else if (propURI.equals(PROP_HELP)) {
425 	    if (value instanceof String)
426 		knownProp = true;
427 	    else
428 		return false;
429 	} else if (propURI.equals(PROP_HINT)) {
430 	    if (value instanceof String)
431 		knownProp = true;
432 	    else
433 		return false;
434 	} else if (propURI.equals(PROP_PARENT_CONTROL)) {
435 	    if (value instanceof FormControl
436 		    || (value instanceof Form && this instanceof Group))
437 		knownProp = true;
438 	    else
439 		return false;
440 	} else if (propURI.equals(PROP_REFERENCED_PPATH)) {
441 	    if (value instanceof PropertyPath)
442 		knownProp = true;
443 	    else
444 		return false;
445 	} else if (propURI.equals(PROP_VALUE_RESTRICTION)) {
446 	    if (value instanceof MergedRestriction
447 		    && !props.containsKey(propURI))
448 		knownProp = true;
449 	    else
450 		return false;
451 	}
452 
453 	if (knownProp) {
454 	    props.put(propURI, value);
455 	    return true;
456 	} else
457 	    return super.setProperty(propURI, value);
458     }
459 
460     /**
461      * Returns the text of the {@link Label} possibly associated with this
462      * control, null otherwise.
463      */
464     public String toString() {
465 	Label l = getLabel();
466 	return (l == null) ? null : l.getText();
467     }
468 }