de.iwes.widgets.pattern.page.impl.PatternPageUtilInternal Maven / Gradle / Ivy
/**
* Copyright 2014-2018 Fraunhofer-Gesellschaft zur Förderung der angewandten Wissenschaften e.V.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package de.iwes.widgets.pattern.page.impl;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.xml.bind.DatatypeConverter;
import org.ogema.core.application.ApplicationManager;
import org.ogema.core.model.Resource;
import org.ogema.core.model.ResourceList;
import org.ogema.core.model.ValueResource;
import org.ogema.core.model.array.ArrayResource;
import org.ogema.core.model.array.BooleanArrayResource;
import org.ogema.core.model.array.ByteArrayResource;
import org.ogema.core.model.array.FloatArrayResource;
import org.ogema.core.model.array.IntegerArrayResource;
import org.ogema.core.model.array.StringArrayResource;
import org.ogema.core.model.array.TimeArrayResource;
import org.ogema.core.model.simple.BooleanResource;
import org.ogema.core.model.simple.FloatResource;
import org.ogema.core.model.simple.IntegerResource;
import org.ogema.core.model.simple.SingleValueResource;
import org.ogema.core.model.simple.StringResource;
import org.ogema.core.model.simple.TimeResource;
import org.ogema.core.resourcemanager.ResourceAccess;
import org.ogema.core.resourcemanager.ResourceNotFoundException;
import org.ogema.core.resourcemanager.pattern.ResourcePattern;
import org.ogema.core.resourcemanager.pattern.ResourcePattern.CreateMode;
import org.ogema.core.resourcemanager.pattern.ResourcePattern.Existence;
import org.slf4j.LoggerFactory;
import de.iwes.widgets.api.extended.OgemaWidgetBase;
import de.iwes.widgets.api.services.NameService;
import de.iwes.widgets.api.widgets.WidgetPage;
import de.iwes.widgets.api.widgets.dynamics.TriggeredAction;
import de.iwes.widgets.api.widgets.dynamics.TriggeringAction;
import de.iwes.widgets.api.widgets.localisation.LocaleDictionary;
import de.iwes.widgets.api.widgets.localisation.OgemaLocale;
import de.iwes.widgets.api.widgets.sessionmanagement.OgemaHttpRequest;
import de.iwes.widgets.html.calendar.datepicker.Datepicker;
import de.iwes.widgets.html.form.dropdown.Dropdown;
import de.iwes.widgets.html.form.dropdown.DropdownOption;
import de.iwes.widgets.html.form.dropdown.DropdownData;
import de.iwes.widgets.html.form.label.Label;
import de.iwes.widgets.html.form.textfield.TextField;
import de.iwes.widgets.html.multiselect.Multiselect;
import de.iwes.widgets.pattern.widget.dropdown.PatternDropdown;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.DefaultValue;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.DisplayValue;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.Entry;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.EntryType;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.FilterString;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.FilterValueFloat;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.FilterValueLong;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.NamingPolicy;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.PreferredName;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.ReferenceRestriction;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.SetValue;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.StringConversion;
import de.iwes.widgets.pattern.widget.patternedit.PatternPageAnnotations.Unmodifiable;
public class PatternPageUtilInternal {
/** throws various exceptions, which will be displayed on UI */
public static void applyInternalAndExternalFilters(Map> fieldTypes, Map> valueWidgets,
Map externalFilters, Map externalRangeFilters, OgemaHttpRequest req) {
OgemaLocale locale = req.getLocale();
for (Map.Entry> entries : fieldTypes.entrySet()) {
String id = entries.getKey();
Class type = entries.getValue();
if (type.equals(Boolean.class)) {
continue; // in this case one can only select from predefined options anyway
}
OgemaWidgetBase widget = valueWidgets.get(id);
if (!(widget instanceof TextField)) // unmodifiable fields // TODO catch empty Dropdowns/Multiselects for non-optional fields or context entries
continue;
TextField tf = (TextField) widget;
String value = tf.getValue(req);
FilterRange filterRange = externalRangeFilters.get(id);
if (type.equals(Float.class)) {
float val;
try {
val = Float.parseFloat(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(LocalisationUtil.filterMsgNotAFloatingPointNr(id, locale));
}
if (filterRange instanceof LongRange) {
long lowerBound = ((LongRange) filterRange).getLowerValue();
long upperBound = ((LongRange) filterRange).getUpperValue();
if (lowerBound != Long.MIN_VALUE && val < lowerBound) throw new IllegalArgumentException(LocalisationUtil.valueTooSmall(id, val, lowerBound, locale));
if (upperBound != Long.MAX_VALUE && val > upperBound) throw new IllegalArgumentException(LocalisationUtil.valueTooLarge(id, val, upperBound, locale));
} else if (filterRange instanceof FloatRange) {
float lowerBound = ((FloatRange) filterRange).getLowerValue();
float upperBound = ((FloatRange) filterRange).getUpperValue();
if (!Float.isNaN(lowerBound)) {
boolean violated = ((FloatRange) filterRange).isLowerIncluded() ? val < lowerBound : val <= lowerBound;
if (violated) throw new IllegalArgumentException(LocalisationUtil.valueTooSmall(id, val, lowerBound, locale));
}
if (!Float.isNaN(upperBound)) {
boolean violated = ((FloatRange) filterRange).isUpperIncluded() ? val > upperBound : val >= upperBound;
if (violated) throw new IllegalArgumentException(LocalisationUtil.valueTooLarge(id, val, upperBound, locale));
}
}
}
else if (type.equals(Integer.class) || type.equals(Long.class)) {
long val;
try {
val = Long.parseLong(value);
} catch (NumberFormatException e) {
throw new IllegalArgumentException(LocalisationUtil.filterMsgNotAnInteger(id, locale));
}
if (filterRange instanceof LongRange) {
long lowerBound = ((LongRange) filterRange).getLowerValue();
long upperBound = ((LongRange) filterRange).getUpperValue();
if (val < lowerBound) throw new IllegalArgumentException(LocalisationUtil.valueTooSmall(id, val, lowerBound, locale));
if (val > upperBound) throw new IllegalArgumentException(LocalisationUtil.valueTooLarge(id, val, upperBound, locale));
}
} else if(type.equals(Byte.class)) {
// check for valid HEX string
try {
DatatypeConverter.parseHexBinary(value);
} catch (IllegalArgumentException e) {
throw new IllegalArgumentException(LocalisationUtil.filterMsgNotAHexValue(id, locale));
}
}
String externalFilter = externalFilters.get(id);
if (externalFilter != null && !value.matches(externalFilter)) {
throw new IllegalArgumentException(LocalisationUtil.filterMsgExternal(id, externalFilter, locale));
}
}
}
/**
* Set resource values, when user clicks Save button
* @return
* null, if no exceptions occured.
* if there were some exceptions setting any fields, they are reported in the return value.
* Map<field name, exception msg>
*/
public static > Map setValues(P newPattern, Field[] fields,
Map references, Map> valueWidgets, OgemaHttpRequest req, ApplicationManager am) {
Boolean[] targetState = FieldUtils.setAccessiblePrivileged(fields);
Map exceptions = null;
try {
for (Field field : fields) {
try {
if (!Resource.class.isAssignableFrom(field.getType())) continue;
Resource res = (Resource) field.get(newPattern);
String name = field.getName();
Boolean reference = references.get(name);
OgemaWidgetBase widget = valueWidgets.get(name);
if (widget == null || widget instanceof Label) continue; // unmodifiable fields
if (!reference) {
// we do not want to create optional String fields for which no value is set
if (!res.isActive() && res instanceof StringResource && widget instanceof TextField && ((TextField) widget).getValue(req).trim().isEmpty())
continue;
res.create(); // required for optional fields
setValue(res,widget,req,am.getResourceAccess());
}
else if (!(widget instanceof ResourceLocationLabel)){ // filter out ReadOnly fields
setReference(res,widget,req, am);
}
} catch (Exception e) {
am.getLogger().error("A pattern value could not be set",e);
// this is problematic; if there is an exception in some resource, all the others won't be dealt with any more, either.
// throw new RuntimeException(LocalisationUtil.errorSettingValue(field.getName(), req.getLocale()), e);
if (exceptions == null)
exceptions = new HashMap();
exceptions.put(field.getName(), LocalisationUtil.errorSettingValue(field.getName(), req.getLocale()) + ", " + e.getMessage());
}
}
} finally {
FieldUtils.setUnaccessiblePrivileged(fields, targetState);
}
return exceptions;
}
public static > void setValues(Class
patternType, P newPattern, Method[] methods,
Map> valueWidgets, OgemaHttpRequest req, ApplicationManager am) {
FieldUtils.setAccessiblePrivileged(methods);
for (Method method: methods) {
SetValue sv = method.getAnnotation(SetValue.class);
if (sv == null) continue;
String targetMethod = sv.target();
if (targetMethod == null || targetMethod.isEmpty())
targetMethod = method.getName();
OgemaWidgetBase widget = valueWidgets.get(targetMethod);
if (!(widget instanceof TextField)) continue; // should not be necessary
String value = ((TextField) widget).getValue(req);
try {
method.invoke(newPattern, value);
} catch (Exception e) {
am.getLogger().warn("Could not invoke pattern method " + method.getName(),e);
}
}
}
public static > void setMethodValues(P newPattern, Method[] methods,
Map> valueWidgets, OgemaHttpRequest req, ApplicationManager am, NameService nameService) {
Boolean[] targetState = FieldUtils.setAccessiblePrivileged(methods);
try {
for (Method method: methods) {
try {
DisplayValue dv = method.getAnnotation(DisplayValue.class);
if (dv == null) continue;
String name = method.getName();
OgemaWidgetBase widget = valueWidgets.get(name);
if (widget instanceof TriggerableMethodLabel) {
((TriggerableMethodLabel) widget).setPattern(newPattern,req);
continue;
}
Object result = method.invoke(newPattern);
StringConversion sc = getConversionMethod(method);
setStaticValue(widget,result, sc, nameService, req);
} catch (Exception e) {
am.getLogger().error("A pattern value could not be set",e);
throw new RuntimeException(LocalisationUtil.errorSettingValue(method.getName(), req.getLocale()), e);
}
}
} finally {
FieldUtils.setUnaccessiblePrivileged(methods, targetState);
}
}
private static StringConversion getConversionMethod(Method method) {
DisplayValue dv = method.getAnnotation(DisplayValue.class);
if (dv == null) return StringConversion.TO_STRING;
return dv.stringConversion();
}
@SuppressWarnings("unchecked")
private static void setStaticValue(OgemaWidgetBase widget, Object value, StringConversion conversionMethod,
NameService nameService, OgemaHttpRequest req) {
if (!(widget instanceof Label) && !(widget instanceof TextField)) {
LoggerFactory.getLogger(PatternPageUtilInternal.class).error("A static pattern entry (method) is assigned a non-Label widget");
return;
}
// Label label = (Label) widget;
String val = null;
switch (conversionMethod) {
case TO_STRING:
val = value.toString();
break;
case NAME_SERVICE:
if (value instanceof Resource) {
Resource resource = (Resource) value;
val = nameService.getName(resource, req.getLocale(), true, true);
if (val == null) val = resource.getLocation();
} else if (value instanceof Class) { // expecting Class, otherwise the simple class name is returned
@SuppressWarnings("rawtypes")
Class clazz = (Class) value;
try {
val = nameService.getName(clazz, req.getLocale(), true);
} catch (Exception e) {
}
if (val == null) val = clazz.getSimpleName();
} else {
LoggerFactory.getLogger(PatternPageUtilInternal.class).error
("Method return type " + value.getClass().getSimpleName() + " not compatible with StringConversion.NAME_SERVICE");
return;
}
break;
case RESOURCE_PATH:
if (!(value instanceof Resource)) {
LoggerFactory.getLogger(PatternPageUtilInternal.class).error
("Method return type " + value.getClass().getSimpleName() + " not compatible with StringConversion.RESOURCE_PATH");
return;
}
Resource resource = (Resource) value;
val = resource.getPath();
break;
default:
LoggerFactory.getLogger(PatternPageUtilInternal.class).error("String conversion method " + conversionMethod + " not yet implemented");
return;
}
if (widget instanceof Label)
((Label) widget).setText(val, req);
else if (widget instanceof TextField)
((TextField) widget).setValue(val, req);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void setValue(final Resource res,OgemaWidgetBase widget, OgemaHttpRequest req, final ResourceAccess ra) {
if (widget instanceof TextField && (res instanceof SingleValueResource || res instanceof ByteArrayResource)) {
String text= ((TextField) widget).getValue(req);
setValue((ValueResource) res,text);
}
else if (widget instanceof EnumDropdown && res instanceof SingleValueResource) {
EnumDropdown ed = (EnumDropdown) widget;
String text = ed.getSelectedValue(req);
Class enType = ed.getEnumType();
Method map = null;
try {
if (text != null && !text.isEmpty()) {
map = enType.getMethod("map", new Class[]{}); // privileged call required?
// read selected enum value and apply the map method to it, to obtain the desired resource value
if (map != null) {
Enum en = Enum.valueOf(enType, text);
text = map.invoke(en).toString();
}
}
} catch (Exception e) {}
if (text == null) text = "";
setValue((SingleValueResource) res,text);
}
else if (widget instanceof BooleanDropdown && res instanceof BooleanResource) {
boolean status = Boolean.parseBoolean(((BooleanDropdown) widget).getSelected(req).id().toLowerCase());
((BooleanResource) res).setValue(status);
}
else if (widget instanceof Datepicker && res instanceof TimeResource) {
long date = ((Datepicker) widget).getDateLong(req);
((TimeResource) res).setValue(date);
}
else if (widget instanceof Multiselect && res instanceof ResourceList) {
Collection selected = (( Multiselect) widget).getSelected(req);
Class clazz = ((ResourceList) res).getElementType();
List oldValues = res.getSubResources(clazz, false);
final Set newValues = new LinkedHashSet();
Iterator it = selected.iterator();
while (it.hasNext()) {
DropdownOption opt =it.next();
String path = opt.getValue();
boolean contained = isResourceContained((List) oldValues,path);
if (!contained)
newValues.add(path);
}
Iterator itRes = oldValues.iterator();
while (itRes.hasNext()) {
Resource rs = itRes.next();
boolean contained = isResourceSelected(selected,rs);
if (!contained) {
((ResourceList) res).remove(ra.getResource(rs.getLocation()));
}
}
if (!newValues.isEmpty()) {
// FIXME privileged execution probably not required any more (tbc)
AccessController.doPrivileged(new PrivilegedAction() {
@Override
public Void run() {
for (String path: newValues) {
final Resource newRes = ra.getResource(path);
if (newRes == null) continue;
try {
// ((ResourceList) res).add(newRes); // FIXME NoSuchMethodError at runtime; with reflections it works -> ??
Method addMethod = ResourceList.class.getMethod("add", new Class[]{Resource.class});
addMethod.invoke(res, newRes);
} catch (Exception e) {
e.printStackTrace();
}
}
return null;
}
});
}
}
else if (widget instanceof EnumMultiselect && res instanceof ArrayResource) {
Collection labels = ((EnumMultiselect) widget).getSelectedValues(req);
try {
setValues((ArrayResource) res, labels);
} catch (Exception e) {
LoggerFactory.getLogger(PatternPageUtilInternal.class).error("Could not set ArrayResource values",e);
}
}
else
throw new IllegalArgumentException("Widget/Value pair could not be handled... resource " + res + "; widget type: " + widget.getClass().getSimpleName());
}
private static void setValues(ArrayResource array, Collection selected) {
int counter= 0;
if (array instanceof IntegerArrayResource) {
int[] arr = new int[selected.size()];
for (String sel: selected) {
arr[counter++] = Integer.parseInt(sel); // TODO exceptions?
}
((IntegerArrayResource) array).setValues(arr);
}
else if (array instanceof StringArrayResource) {
String[] arr = new String[selected.size()];
for (String sel: selected) {
arr[counter++] = sel; // TODO exceptions?
}
((StringArrayResource) array).setValues(arr);
}
else if (array instanceof FloatArrayResource) {
float[] arr = new float[selected.size()];
for (String sel: selected) {
arr[counter++] = Float.parseFloat(sel); // TODO exceptions?
}
((FloatArrayResource) array).setValues(arr);
}
else if (array instanceof BooleanArrayResource) {
boolean[] arr = new boolean[selected.size()];
for (String sel: selected) {
arr[counter++] = Boolean.parseBoolean(sel); // TODO exceptions?
}
((BooleanArrayResource) array).setValues(arr);
}
else if (array instanceof TimeArrayResource) {
long[] arr = new long[selected.size()];
for (String sel: selected) {
arr[counter++] = Long.parseLong(sel); // TODO exceptions?
}
((TimeArrayResource) array).setValues(arr);
}
}
private static void setValue(ValueResource svr, String text) {
if (svr instanceof StringResource) {
((StringResource) svr).setValue(text);
}
else if (svr instanceof FloatResource) {
((FloatResource) svr).setValue(Float.parseFloat(text));
} else if (svr instanceof IntegerResource) {
((IntegerResource) svr).setValue(Integer.parseInt(text));
} else if (svr instanceof TimeResource) {
((TimeResource) svr).setValue(Long.parseLong(text));
} else if (svr instanceof BooleanResource) {
((BooleanResource) svr).setValue(Boolean.parseBoolean(text));
} else if (svr instanceof ByteArrayResource) {
((ByteArrayResource) svr).setValues(DatatypeConverter.parseHexBinary(text));
}
else
throw new RuntimeException("Invalid resource type " + svr.getResourceType().getSimpleName());
}
private static void setReference(Resource res,OgemaWidgetBase widget, OgemaHttpRequest req, ApplicationManager am) {
if (!(widget instanceof ReferenceDropdown)) {
throw new IllegalArgumentException("ReferenceDropdown widget expected");
}
@SuppressWarnings("rawtypes")
ReferenceDropdown refWidget = (ReferenceDropdown) widget;
DropdownOption opt = refWidget.getSelected(req);
if (opt == null)
throw new IllegalArgumentException("Please select an item for " + widget.getId());
String path = opt.id();
if (path.equals(DropdownData.EMPTY_OPT_ID)) {
if (res.exists() && res.isReference(false)) {
res.delete(); // remove reference
}
return;
}
Resource target = am.getResourceAccess().getResource(path);
if (target == null)
throw new ResourceNotFoundException("Resource " + path + " not found");
res.setAsReference(target);
}
/*
* methods only required for EditPage, can be null or empty for PageCreator
* mainSelector: null for PageCreator
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public static void createWidgets(final WidgetPage page, String widgetID, Field[] fields, Method[] methods, Field[] contextFields, Map> labelWidgets,
Map> valueWidgets, Map references, Map> fieldTypes, Map externalFilters, Map externalRangeFilters,
PatternDropdown mainSelector,
boolean setDefaults, final ApplicationManager am, final Class dictionary, boolean forceEditable, boolean forceUnmodifiable) {
if (forceEditable && forceUnmodifiable)
throw new IllegalArgumentException("forceEditable and forceUnmodifiable must not both be true");
// set method widgets first, then field widgets
Boolean[] accessibleStates;
// D dict = page.getDictionary("en"); // FIXME at this point the localisations are not registered yet;
// need to generate labels dynamically anyways...
if (methods != null && methods.length > 0) {
accessibleStates = FieldUtils.setAccessiblePrivileged(methods);
try {
List referencedMethods = getReferencedMethods(methods);
DisplayValue dv;
SetValue sv;
for (Method method: methods) {
dv = method.getAnnotation(DisplayValue.class);
sv = method.getAnnotation(SetValue.class);
if (dv == null && sv == null) continue; // should not happen; a selection has taken place previously; note: this filters out
// methods with a @SetValue annotation
final String id = method.getName();
Class returnType = method.getReturnType();
OgemaWidgetBase methodLabel;
OgemaWidgetBase methodValue;
if (dv != null && dv.useTriggerButton() && !referencedMethods.contains(id)) {
methodValue = new TriggerableMethodLabel(page, widgetID + "_" + id + "_valueWidget", method);
methodLabel = new MethodTriggerButton(page, widgetID + "_" + id + "_labelWidget", id, (TriggerableMethodLabel) methodValue, dictionary);
methodLabel.triggerAction(methodValue, TriggeringAction.POST_REQUEST, TriggeredAction.GET_REQUEST);
}
else {
if (dv != null) {
StringConversion sc = dv.stringConversion();
if (sc == StringConversion.NAME_SERVICE) {
if (!Resource.class.isAssignableFrom(returnType) && !Class.class.isAssignableFrom(returnType)) {
am.getLogger().error("Method return type of " + id + " not applicable to NameService");
continue;
}
}
if (referencedMethods.contains(id))
methodValue = new TextField(page, widgetID + "_" + id + "_valueWidget", "");
else
methodValue = new Label(page, widgetID + "_" + id + "_valueWidget", "");
}
else { //if (sv != null) {
String target = sv.target();
if (hasReferencedMethod(method, methods)) continue;
methodValue = new TextField(page, widgetID + "_" + id + "_valueWidget","");
}
methodLabel = new PatternEntryLabel(page,widgetID + "_" + id + "_labelWidget",id,dictionary);
}
labelWidgets.put(id, methodLabel);
valueWidgets.put(id, methodValue);
}
} finally {
FieldUtils.setUnaccessiblePrivileged(methods, accessibleStates);
}
}
// Map
final Map allFields = new LinkedHashMap<>();
if (contextFields != null) {
FieldUtils.setAccessiblePrivileged(contextFields);
for (Field f: contextFields) {
allFields.put(f, true);
}
}
for (Field f: fields) {
allFields.put(f, false);
}
// field widgets
accessibleStates = FieldUtils.setAccessiblePrivileged(fields);
try {
Class clazz;
Entry entry;
EntryType type;
String id;
OgemaWidgetBase value;
Label label;
boolean editable;
Unmodifiable unmod;
Field field;
for (Map.Entry mentry: allFields.entrySet()) {
field = mentry.getKey();
clazz = field.getType();
if (!Resource.class.isAssignableFrom(clazz) && !mentry.getValue())
continue;
entry = field.getAnnotation(Entry.class);
if (entry == null && !ValueResource.class.isAssignableFrom(clazz) && !ResourceList.class.isAssignableFrom(clazz)) continue; // default for complex resources: not shown
else if (entry != null && !entry.show()) continue;
type = field.getAnnotation(EntryType.class);
boolean reference;
boolean rawData = false;
if (type == null) { // use default setting
if (ValueResource.class.isAssignableFrom(clazz) || ResourceList.class.isAssignableFrom(clazz))
reference = false;
else
reference = true;
}
else {
reference = type.setAsReference(); // FIXME does it makes sense for complex resources not to set them as references?
rawData = type.rawData();
}
id = field.getName();
value = null;
editable = true;
if (forceUnmodifiable)
editable = false;
else if (!forceEditable) {
unmod = field.getAnnotation(Unmodifiable.class);
if (unmod != null)
editable = false;
}
if ((!reference && ValueResource.class.isAssignableFrom(clazz)) || !Resource.class.isAssignableFrom(clazz)) { // the latter case covers the context widgets
String defaultString = null;
if (setDefaults) {
DefaultValue defaultValue = field.getAnnotation(DefaultValue.class);
if (defaultValue != null)
defaultString = defaultValue.value();
}
Class enumType = null;
if (type != null)
enumType = type.enumType();
if (enumType != null && !enumType.equals(Enum.class)) {
if (SingleValueResource.class.isAssignableFrom(clazz) || ByteArrayResource.class.isAssignableFrom(clazz)) {
value = new EnumDropdown(page, widgetID + "_" + id, enumType);
if (defaultString != null && ((EnumDropdown) value).getValidOptions().contains(defaultString))
((EnumDropdown) value).selectDefault(defaultString);
}
else { // ArrayResource
value = new EnumMultiselect(page, widgetID + "_" + id, enumType);
// TODO select defaults based on defaultString
}
}
else
value = createSimpleWidget(page, (Class) clazz, id, defaultString, editable, widgetID, rawData);
references.put(id, false);
setType(id,clazz, fieldTypes);
FilterString filter = field.getAnnotation(FilterString.class);
if (filter != null)
externalFilters.put(id, filter.regexp());
FilterValueLong filterValue = field.getAnnotation(FilterValueLong.class);
if (filterValue != null)
externalRangeFilters.put(id, new LongRange(filterValue.lowerBound(), filterValue.upperBound()));
FilterValueFloat filterValueFloat = field.getAnnotation(FilterValueFloat.class);
if (filterValueFloat != null)
externalRangeFilters.put(id, new FloatRange(filterValueFloat.lowerBound(), filterValueFloat.upperBound(), filterValueFloat.includeLowerBoundary(), filterValueFloat.includeUpperBoundary()));
}
else if (!reference && ResourceList.class.isAssignableFrom(clazz)) {
ReferenceRestriction restr = field.getAnnotation(ReferenceRestriction.class);
if (restr == null) continue; // cannot infer type of the ResourceList
Class> targetPattern =restr.targetPattern();
NamingPolicy naming = field.getAnnotation(NamingPolicy.class);
PreferredName preferredName;
if (naming != null)
preferredName = naming.policy();
else
preferredName = PreferredName.USER_GIVEN_NAME;
value = new ResourceListMultiselect(page, widgetID + "_" + id, targetPattern, preferredName, am);
references.put(id, false);
setType(id,clazz, fieldTypes);
}
else if (reference) {
ReferenceRestriction restr = field.getAnnotation(ReferenceRestriction.class);
Class targetRestr = null;
if (restr != null) {
targetRestr = restr.targetPattern();
}
NamingPolicy naming = field.getAnnotation(NamingPolicy.class);
PreferredName preferredName;
if (naming != null)
preferredName = naming.policy();
else
preferredName = PreferredName.USER_GIVEN_NAME;
Existence existence = field.getAnnotation(Existence.class);
boolean required = true;
if (existence != null && existence.required() == CreateMode.OPTIONAL)
required = false;
value = createComplexWidget(page,(Class) clazz, targetRestr, mainSelector, field, id, preferredName, required, am, editable, widgetID);
references.put(id, true);
}
// TODO others
if (value != null) {
// Label label = new Label(page, id + "_label",true);
label = new PatternEntryLabel(page, id + "_label", id, dictionary);
labelWidgets.put(id, label);
valueWidgets.put(id, value);
}
}
} finally {
FieldUtils.setUnaccessiblePrivileged(fields, accessibleStates);
}
}
private static List getReferencedMethods(Method[] methods) {
List list = new ArrayList();
for (Method method: methods) {
SetValue sv = method.getAnnotation(SetValue.class);
if (sv ==null) continue;
String target = sv.target();
if (target != null && !target.isEmpty())
list.add(target);
else
list.add(method.getName()); // use the method's name as default
}
return list;
}
private static boolean hasReferencedMethod(Method method, Method[] methods) {
SetValue sv = method.getAnnotation(SetValue.class);
if (sv ==null) return false;
String target = sv.target();
if (target == null || target.isEmpty())
target = method.getName();
for (Method mthd : methods) {
if (mthd.equals(method)) continue;
if (mthd.getName().equals(target) && mthd.getParameterTypes().length == 0)
return true;
}
return false;
}
private static OgemaWidgetBase createSimpleWidget(WidgetPage page, Class type, String name, String defaultValue, boolean editable, String widgetID, boolean rawData) {
if (editable && BooleanResource.class.isAssignableFrom(type)) { // TODO implement editable for booleanREsource
BooleanDropdown dd = new BooleanDropdown(page, widgetID + "_" + name, defaultValue);
// TODO set default value
return dd;
}
else if (TimeResource.class.isAssignableFrom(type) && !rawData) { // TODO !editable
Datepicker dp = new Datepicker(page, widgetID + "_" + name);
dp.setDefaultDate(defaultValue); // TODO
return dp;
}
else {
if (editable) {
TextField tf = new TextField(page,widgetID + "_" + name);
if (defaultValue != null) { // TODO filter for admissible values?
tf.setDefaultValue(defaultValue);
}
return tf;
}
else { // TODO ensure this can be handled in POST of selector
Label lab = new Label(page,widgetID + "_" + name);
return lab;
}
}
}
// patternType may be null
@SuppressWarnings({ "rawtypes", "unchecked" })
private static OgemaWidgetBase createComplexWidget(WidgetPage page, Class clazz, Class targetRestr,
PatternDropdown mainSelector, Field targetField,
String name, PreferredName preferredName, boolean required, ApplicationManager am, boolean editable, String widgetID) {
if (editable) {
Dropdown referencesDropdown = new ReferenceDropdown(page,widgetID + "_" + name, clazz, preferredName, am, mainSelector, targetField, !required, targetRestr);
return referencesDropdown;
}
else {
Label label = new ResourceLocationLabel(page,widgetID + "_" + name, preferredName, am.getResourceAccess());
return label;
}
}
private static void setType(String id, Class clazz, Map> fieldTypes) {
// if (!SingleValueResource.class.isAssignableFrom(clazz))
// return;
if (StringResource.class.isAssignableFrom(clazz))
fieldTypes.put(id, String.class);
else if (IntegerResource.class.isAssignableFrom(clazz))
fieldTypes.put(id, Integer.class);
else if (FloatResource.class.isAssignableFrom(clazz))
fieldTypes.put(id, Float.class);
else if (TimeResource.class.isAssignableFrom(clazz))
fieldTypes.put(id, Long.class);
else if (BooleanResource.class.isAssignableFrom(clazz))
fieldTypes.put(id, Boolean.class);
else if (ResourceList.class.isAssignableFrom(clazz))
fieldTypes.put(id, List.class);
else if (ByteArrayResource.class.isAssignableFrom(clazz))
fieldTypes.put(id, Byte.class);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
public static void setWidgetValue(OgemaWidgetBase widget, Resource resource, OgemaHttpRequest req) {
if (widget instanceof ControllableDropdown) {
((ControllableDropdown) widget).setActive(true, req);
}
if (widget instanceof ResourceLocationLabel) { // read-only reference field
((ResourceLocationLabel) widget).setValue(resource, req);
}
else if (widget instanceof Datepicker) {
((Datepicker) widget).setDate(((TimeResource) resource).getValue(), req);
}
else if ((widget instanceof TextField || widget instanceof Label || widget instanceof EnumDropdown) && (resource instanceof SingleValueResource || resource instanceof ByteArrayResource) ) {
setWidgetValueSimple(widget,(ValueResource) resource,req);
}
else if (widget instanceof BooleanDropdown && resource instanceof BooleanResource) {
boolean bool = ((BooleanResource) resource).getValue();
((BooleanDropdown) widget).selectSingleOption(bool ? "TRUE" : "FALSE", req);
}
else if (widget instanceof ReferenceDropdown) {
ReferenceDropdown dd = (ReferenceDropdown) widget;
if (!resource.isActive()) {
dd.selectSingleOption(DropdownData.EMPTY_OPT_ID, req);
}
else {
String loc = resource.getLocation();
dd.selectSingleOption(loc, req);
}
}
else if (widget instanceof ResourceListMultiselect && resource instanceof ResourceList) {
((ResourceListMultiselect) widget).setActive(true, req);
setWidgetValueMulti((ResourceListMultiselect) widget,(ResourceList) resource, req);
}
else if (widget instanceof EnumMultiselect && resource instanceof ArrayResource) {
((EnumMultiselect) widget).setActive(true, req);
setWidgetValueArray((EnumMultiselect) widget, (ArrayResource) resource,req);
}
}
private static List getArrayValues(ArrayResource resource) {
List values = new ArrayList();
if (resource instanceof IntegerArrayResource) {
int[] vals = ((IntegerArrayResource) resource).getValues();
for (int val: vals) {
values.add(String.valueOf(val));
}
}
else if (resource instanceof StringArrayResource) {
String[] vals = ((StringArrayResource) resource).getValues();
for (String val: vals) {
values.add(val);
}
}
else if (resource instanceof FloatArrayResource) {
float[] vals = ((FloatArrayResource) resource).getValues();
for (float val: vals) {
values.add(String.valueOf(val));
}
}
else if (resource instanceof BooleanArrayResource) {
boolean[] vals = ((BooleanArrayResource) resource).getValues();
for (boolean val: vals) {
values.add(String.valueOf(val));
}
}
else if (resource instanceof TimeArrayResource) {
long[] vals = ((TimeArrayResource) resource).getValues();
for (long val: vals) {
values.add(String.valueOf(val));
}
}
return values;
}
private static void setWidgetValueArray(EnumMultiselect widget, ArrayResource resource, OgemaHttpRequest req) {
// widget.setSelectedValues(getArrayValues(resource), req);
widget.selectMultipleOptions(getArrayValues(resource), req);
}
@SuppressWarnings({ "rawtypes", "unchecked" })
private static void setWidgetValueSimple(OgemaWidgetBase widget,ValueResource res, OgemaHttpRequest req) {
String value;
if (res.isActive())
value = getResourceValueSimple(res);
else
value = "";
if (widget instanceof TextField)
((TextField) widget).setValue(value, req);
else if (widget instanceof Label)
((Label) widget).setText(value, req);
else if (widget instanceof EnumDropdown) {
EnumDropdown ed = (EnumDropdown) widget;
Class enType = ed.getEnumType();
Method map = null;
// Class returnType = null;
try {
if (value != null && !value.isEmpty()) {
map = enType.getMethod("map", new Class[]{}); // privileged call required?
// returnType = map.getReturnType();
// read widget value and try to find a corresponding enum that is mapped to this value
if (map != null)
value = getEnumNameForMapResult(value, map, enType.getEnumConstants());
}
} catch (Exception e) {}
// TODO remove
// this is the inverse method, required to set the widget value
// try {
// Enum selected = Enum.valueOf(enType, value);
// Object obj = map.invoke(selected)
// } catch (Exception e) {
//
// }
if (!ed.getValidOptions().contains(value))
LoggerFactory.getLogger(PatternPageUtilInternal.class).warn("Resource value " + value
+ " does not correspond to enum type " + ed.getEnumType().getSimpleName());
else
ed.selectSingleOption(value, req);
}
else
throw new IllegalArgumentException("Unsupported widget type + " + widget.getWidgetClass());
}
@SuppressWarnings("rawtypes")
private static String getEnumNameForMapResult(String widgetValueAsString, Method map, Enum[] enums) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException {
for (Enum en: enums) {
String mapResult = map.invoke(en).toString();
if (mapResult != null && mapResult.equals(widgetValueAsString))
return en.name();
}
return null;
}
private static String getResourceValueSimple(ValueResource res) {
String value;
if (res instanceof StringResource)
value = ((StringResource) res).getValue();
else if (res instanceof IntegerResource)
value = String.valueOf(((IntegerResource) res).getValue());
else if (res instanceof FloatResource)
value = String.valueOf(((FloatResource) res).getValue());
else if (res instanceof TimeResource)
value = String.valueOf(((TimeResource) res).getValue());
else if (res instanceof BooleanResource)
value = String.valueOf(((BooleanResource) res).getValue());
else if (res instanceof ByteArrayResource)
value = DatatypeConverter.printHexBinary(((ByteArrayResource) res).getValues());
else throw new IllegalArgumentException("Unexpected resource type");
return value;
}
private static void setWidgetValueMulti(ResourceListMultiselect widget,ResourceList list, OgemaHttpRequest req) {
Set selectedOptions = new LinkedHashSet();
List entries = list.getAllElements();
for (Resource res :entries) {
selectedOptions.add(res.getLocation());
}
widget.selectMultipleOptions(selectedOptions, req);
}
public static void clearWidgets(Map> widgets, OgemaHttpRequest req) {
for (OgemaWidgetBase widget: widgets.values()) {
if (widget instanceof TextField) {
((TextField) widget).setValue("", req);
}
else if (widget instanceof ReferenceDropdown) {
((ReferenceDropdown) widget).selectSingleOption(DropdownData.EMPTY_OPT_ID, req);
}
else if (widget instanceof ControllableDropdown) {
((ControllableDropdown) widget).setActive(false, req);
}
else if (widget instanceof ResourceListMultiselect) {
((ResourceListMultiselect) widget).setActive(false, req);
}
else if (widget instanceof TriggerableMethodLabel) {
((TriggerableMethodLabel) widget).setMessage("", req);
((TriggerableMethodLabel) widget).setPattern(null, req);
}
else if (widget instanceof Label) {
((Label) widget).setText("", req);
}
else if (widget instanceof Datepicker) {
((Datepicker) widget).setDate("", req);
}
else if (widget instanceof EnumMultiselect) {
((EnumMultiselect) widget).setActive(false, req);
}
}
}
private static boolean isResourceContained(List list,String path) {
for (Resource res: list) {
if (res.getPath().equals(path) || res.getLocation().equals(path))
return true;
}
return false;
}
private static boolean isResourceSelected(Collection list,Resource res) {
for (DropdownOption opt: list) {
String path = opt.id();
if (res.getPath().equals(path) || res.getLocation().equals(path))
return true;
}
return false;
}
}