package org.orienteer.core; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Objects; import org.apache.wicket.extensions.markup.html.repeater.data.sort.SortOrder; import org.apache.wicket.util.string.Strings; import com.orientechnologies.orient.core.metadata.schema.OClass; import com.orientechnologies.orient.core.metadata.schema.OClassImpl; import com.orientechnologies.orient.core.metadata.schema.OProperty; import com.orientechnologies.orient.core.metadata.schema.OType; /** * Orienteer needs in additional attributes specified for {@link OClass} and {@link OProperty} * This class allow flexibly access to that custom parameters. */ public final class CustomAttribute { private static final Map<String, CustomAttribute> CACHE = new HashMap<String, CustomAttribute>(); /** * Is this property calculable or not */ public static final CustomAttribute CALCULABLE = create("orienteer.calculable", OType.BOOLEAN, false, false, true); /** * Script to calculate value of the property */ public static final CustomAttribute CALC_SCRIPT = create("orienteer.script", OType.STRING, null, true, true); /** * Is this property displayable or not */ public static final CustomAttribute DISPLAYABLE = create("orienteer.displayable", OType.BOOLEAN, false, false, true); /** * Is this property should be readonly in UI */ public static final CustomAttribute UI_READONLY = create("orienteer.uireadonly", OType.BOOLEAN, false, false, true); /** * Is this property value should not be visible */ public static final CustomAttribute HIDDEN = create("orienteer.hidden", OType.BOOLEAN, false, false, true); /** * Order of this property in a table/tab */ public static final CustomAttribute ORDER = create("orienteer.order", OType.INTEGER, 0, false, true); /** * Name of the tab where this parameter should be shown */ public static final CustomAttribute TAB = create("orienteer.tab", OType.STRING, null, false, true); /** * Name of the property which is storing name of this entity */ public static final CustomAttribute PROP_NAME = create("orienteer.prop.name", OType.LINK, OProperty.class, null, false, true); /** * Name of property which is storing link to a parent entity */ public static final CustomAttribute PROP_PARENT = create("orienteer.prop.parent", OType.LINK, OProperty.class, null, false, true); /** * Name of a visualization that should be used for property visualization */ public static final CustomAttribute VISUALIZATION_TYPE = create("orienteer.visualization", OType.STRING, "default", false, true); /** * Link to an inverse property in respect to this one */ public static final CustomAttribute PROP_INVERSE = create("orienteer.inverse", OType.LINK, OProperty.class, null, false, true); /** * Description of {@link OProperty} or {@link OClass} */ public static final CustomAttribute DESCRIPTION = create("orienteer.description",OType.STRING,null,true, false); /** * Access levels ("_allow", "_allowRead", "_allowUpdate", or "_allowDelete") granted on document creation. */ public static final CustomAttribute ON_CREATE_FIELDS = create("onCreate.fields",OType.STRING,null,true, true); /** * Identity type ("user" or "role") who will get access rights on document creation. */ public static final CustomAttribute ON_CREATE_IDENTITY_TYPE = create("onCreate.identityType",OType.STRING,null,true, true); /** * Property name by which to sort data by default */ public static final CustomAttribute SORT_BY = create("orienteer.sortby", OType.LINK, OProperty.class, null, false, true); /** * Order in which to sort data */ public static final CustomAttribute SORT_ORDER = create("orienteer.sortorder", OType.BOOLEAN, null, null, false, true); /** * Default search query for class */ public static final CustomAttribute SEARCH_QUERY = create("orienteer.searchquery", OType.STRING, null, null, true, true); /** * Domain of a class */ public static final CustomAttribute DOMAIN = create("orienteer.domain", OType.STRING, OClassDomain.class, OClassDomain.BUSINESS, false, true); private final String name; private final OType type; private final Object defaultValue; private final Class<?> javaClass; private final boolean encode; private final boolean hiearchical; private CustomAttribute(String name, OType type, Class<?> javaClass, Object defaultValue, boolean encode, boolean hiearchical) { this.name = name; this.type = type; this.javaClass = javaClass!=null?javaClass:type.getDefaultJavaType(); this.defaultValue = defaultValue; this.encode = encode; this.hiearchical = hiearchical; CACHE.put(name, this); } /*public static CustomAttribute create(String name, OType type, Object defaultValue, boolean encode) { return create(name, type, null, defaultValue, encode, false); }*/ /*public static CustomAttribute create(String name, OType type, Class<?> javaClass, Object defaultValue, boolean encode) { return create(name, type, javaClass, defaultValue, encode, false); }*/ public static CustomAttribute create(String name, OType type, Object defaultValue, boolean encode, boolean hiearchical) { return create(name, type, null, defaultValue, encode, hiearchical); } public static CustomAttribute create(String name, OType type, Class<?> javaClass, Object defaultValue, boolean encode, boolean hiearchical) { CustomAttribute ret = getIfExists(name); if(ret!=null) throw new IllegalArgumentException("Custom attribute with name '"+name+"' is already exist"); ret = new CustomAttribute(name, type, javaClass, defaultValue, encode, hiearchical); return ret; } public static CustomAttribute get(String name) { CustomAttribute ret = getIfExists(name); if(ret==null) throw new IllegalArgumentException("Custom attribute with name '"+name+"' was not found"); return ret; } public static CustomAttribute getIfExists(String name) { return CACHE.get(name); } public static Collection<CustomAttribute> values() { return Collections.unmodifiableCollection(CACHE.values()); } public String getName() { return name; } public boolean match(String critery) { return name.equals(critery); } public boolean matchAny(String... criteries) { if(criteries==null || criteries.length==0) return false; for(String critery : criteries) { if(name.equals(critery)) return true; } return false; } public boolean matchAny(CustomAttribute... criteries) { if(criteries==null || criteries.length==0) return false; for(CustomAttribute critery : criteries) { if(equals(critery)) return true; } return false; } public OType getType() { return type; } public Class<?> getJavaClass() { return javaClass; } public Object getDefaultValue() { return defaultValue; } public boolean isEncode() { return encode; } @SuppressWarnings("unchecked") public <V> V getValue(OProperty property) { return getValue(property, (V) defaultValue); } @SuppressWarnings("unchecked") public <V> V getValue(OProperty property, V defaultValue) { String stringValue = property.getCustom(name); if(encode) stringValue = decodeCustomValue(stringValue); V ret; if(OProperty.class.isAssignableFrom(javaClass)) { ret = (V)resolveProperty(property.getOwnerClass(), stringValue); } else { ret = (V) OType.convert(stringValue, javaClass); } return ret!=null?ret:defaultValue; } public <V> void setValue(OProperty property, V value) { if(OProperty.class.isAssignableFrom(javaClass) && value instanceof OProperty) { OProperty valueProperty = (OProperty)value; boolean fullNameRequired = !Objects.equals(property.getOwnerClass(), valueProperty.getOwnerClass()); property.setCustom(name, fullNameRequired?valueProperty.getFullName():valueProperty.getName()); } else { if(defaultValue!=null && defaultValue.equals(value)) value = null; String stringValue = value!=null?value.toString():null; if(stringValue!=null && stringValue.length()==0) stringValue=null; if(encode) stringValue = encodeCustomValue(stringValue); property.setCustom(name, stringValue); } } public <V> V getValue(OClass oClass) { return getValue(oClass, hiearchical); } @SuppressWarnings("unchecked") public <V> V getValue(OClass oClass, boolean hiearchical) { return getValue(oClass, (V) defaultValue, hiearchical); } public <V> V getValue(OClass oClass, V defaultValue) { return getValue(oClass, defaultValue, hiearchical); } @SuppressWarnings("unchecked") public <V> V getValue(OClass oClass, V defaultValue, boolean hiearchical) { String stringValue = oClass.getCustom(name); if(encode) stringValue = decodeCustomValue(stringValue); V ret; if(OProperty.class.isAssignableFrom(javaClass)) { ret = (V)resolveProperty(oClass, stringValue); } else { ret = (V) OType.convert(stringValue, javaClass); } if(ret==null && hiearchical) { for(OClass superClass : oClass.getSuperClasses()) { if((ret=getValue(superClass, null, true))!=null) break; } } return ret!=null?ret:defaultValue; } public <V> void setValue(OClass oClass, V value) { setValue(oClass, value, hiearchical); } public <V> void setValue(OClass oClass, V value, boolean hiearchical) { if(hiearchical && Objects.equals(value, getValue(oClass, true))) return; if(OProperty.class.isAssignableFrom(javaClass) && value instanceof OProperty) { OProperty valueProperty = (OProperty)value; boolean fullNameRequired = !Objects.equals(oClass, valueProperty.getOwnerClass()); oClass.setCustom(name, fullNameRequired?valueProperty.getFullName():valueProperty.getName()); } else { if(!hiearchical && defaultValue!=null && defaultValue.equals(value)) value = null; String stringValue = value!=null?value.toString():null; if(stringValue!=null && stringValue.length()==0) stringValue=null; if(encode) stringValue = encodeCustomValue(stringValue); oClass.setCustom(name, stringValue); } } private OProperty resolveProperty(OClass oClass, String propertyName) { if(Strings.isEmpty(propertyName)) return null; int indx = propertyName.indexOf('.'); if(indx>0) { String className = propertyName.substring(0, indx); propertyName = propertyName.substring(indx+1); oClass = ((OClassImpl)oClass).getOwner().getClass(className); if(oClass==null) return null; } return oClass.getProperty(propertyName); } public static boolean match(String critery, CustomAttribute... attrs) { for (CustomAttribute customAttributes : attrs) { if(customAttributes.match(critery)) return true; } return false; } public static String encodeCustomValue(String value) { if(value==null) return null; StringBuilder sb = new StringBuilder(value.length()); for(int i=0; i<value.length();i++) { char ch = value.charAt(i); switch (ch) { case '=': sb.append("\\e"); break; case '\\': sb.append("\\\\"); break; case '\r': sb.append("\\r"); break; case '\n': sb.append("\\n"); break; default: sb.append(ch); break; } } return sb.toString(); } public static String decodeCustomValue(String value) { if(value==null) return null; StringBuilder sb = new StringBuilder(value.length()); for(int i=0; i<value.length();i++) { char ch = value.charAt(i); if(ch!='\\') { sb.append(ch); } else { if(++i>=value.length()) { sb.append('\\'); break; } else { ch = value.charAt(i); switch (ch) { case 'e': sb.append('='); break; case '\\': sb.append('\\'); break; case 'r': sb.append('\r'); break; case 'n': sb.append('\n'); break; default: sb.append('\\').append(ch); break; } } } } return sb.toString(); } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((name == null) ? 0 : name.hashCode()); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; CustomAttribute other = (CustomAttribute) obj; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; return true; } }