package org.orienteer.core.component.meta;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import org.apache.wicket.Component;
import org.apache.wicket.ajax.AjaxRequestTarget;
import org.apache.wicket.ajax.form.AjaxFormSubmitBehavior;
import org.apache.wicket.ajax.form.OnChangeAjaxBehavior;
import org.apache.wicket.core.util.lang.PropertyResolver;
import org.apache.wicket.markup.html.basic.Label;
import org.apache.wicket.markup.html.basic.MultiLineLabel;
import org.apache.wicket.markup.html.form.CheckBox;
import org.apache.wicket.markup.html.form.ChoiceRenderer;
import org.apache.wicket.markup.html.form.DropDownChoice;
import org.apache.wicket.markup.html.form.IChoiceRenderer;
import org.apache.wicket.markup.html.form.TextArea;
import org.apache.wicket.markup.html.form.TextField;
import org.apache.wicket.model.AbstractReadOnlyModel;
import org.apache.wicket.model.IModel;
import org.apache.wicket.model.LoadableDetachableModel;
import org.apache.wicket.model.PropertyModel;
import org.apache.wicket.util.string.Strings;
import org.apache.wicket.validation.IValidator;
import org.orienteer.core.CustomAttribute;
import org.orienteer.core.OrienteerWebApplication;
import org.orienteer.core.behavior.RefreshMetaContextOnChangeBehaviour;
import org.orienteer.core.component.property.BooleanViewPanel;
import org.orienteer.core.component.property.DisplayMode;
import org.orienteer.core.component.property.OClassViewPanel;
import org.orienteer.core.component.property.OPropertyViewPanel;
import org.orienteer.core.component.visualizer.UIVisualizersRegistry;
import org.orienteer.core.model.ListAvailableOTypesModel;
import org.orienteer.core.model.ListOClassesModel;
import ru.ydn.wicket.wicketorientdb.OrientDbWebSession;
import ru.ydn.wicket.wicketorientdb.model.AbstractNamingModel;
import ru.ydn.wicket.wicketorientdb.model.ListOPropertiesModel;
import ru.ydn.wicket.wicketorientdb.model.OClassNamingModel;
import ru.ydn.wicket.wicketorientdb.model.SimpleNamingModel;
import ru.ydn.wicket.wicketorientdb.proto.OPropertyPrototyper;
import ru.ydn.wicket.wicketorientdb.utils.OClassChoiceRenderer;
import ru.ydn.wicket.wicketorientdb.validation.OSchemaNamesValidator;
import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Collections2;
import com.google.common.collect.Iterables;
import com.google.common.collect.Iterators;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import com.orientechnologies.common.thread.OPollerThread;
import com.orientechnologies.orient.core.collate.OCollate;
import com.orientechnologies.orient.core.collate.OCollateFactory;
import com.orientechnologies.orient.core.db.document.ODatabaseDocument;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.sql.OSQLEngine;
/**
* Meta panel for {@link OProperty}
*
* @param <V> type of a value
*/
public class OPropertyMetaPanel<V> extends AbstractComplexModeMetaPanel<OProperty, DisplayMode, String, V> implements IDisplayModeAware
{
public static final List<String> OPROPERTY_ATTRS = new ArrayList<String>();
public static final List<OType> LINKED_TYPE_OPTIONS;
static
{
OPROPERTY_ATTRS.add(OPropertyPrototyper.NAME);
OPROPERTY_ATTRS.add(CustomAttribute.DESCRIPTION.getName());
OPROPERTY_ATTRS.add(CustomAttribute.TAB.getName());
OPROPERTY_ATTRS.add(CustomAttribute.ORDER.getName());
OPROPERTY_ATTRS.add(OPropertyPrototyper.TYPE);
OPROPERTY_ATTRS.add(CustomAttribute.VISUALIZATION_TYPE.getName());
OPROPERTY_ATTRS.add(OPropertyPrototyper.LINKED_TYPE);
OPROPERTY_ATTRS.add(OPropertyPrototyper.LINKED_CLASS);
OPROPERTY_ATTRS.add(CustomAttribute.PROP_INVERSE.getName());
OPROPERTY_ATTRS.add(OPropertyPrototyper.MANDATORY);
OPROPERTY_ATTRS.add(OPropertyPrototyper.READONLY);
OPROPERTY_ATTRS.add(CustomAttribute.UI_READONLY.getName());
OPROPERTY_ATTRS.add(OPropertyPrototyper.NOT_NULL);
OPROPERTY_ATTRS.add(OPropertyPrototyper.MIN);
OPROPERTY_ATTRS.add(OPropertyPrototyper.MAX);
OPROPERTY_ATTRS.add(OPropertyPrototyper.REGEXP);
OPROPERTY_ATTRS.add(OPropertyPrototyper.COLLATE);
OPROPERTY_ATTRS.add(CustomAttribute.DISPLAYABLE.getName());
OPROPERTY_ATTRS.add(CustomAttribute.HIDDEN.getName());
OPROPERTY_ATTRS.add(CustomAttribute.CALCULABLE.getName());
OPROPERTY_ATTRS.add(CustomAttribute.CALC_SCRIPT.getName());
OPROPERTY_ATTRS.add(OPropertyPrototyper.DEFAULT_VALUE);
// Only single value types are allowed for linked type.
LINKED_TYPE_OPTIONS = ListAvailableOTypesModel.orderTypes(Collections2.filter(Arrays.asList(OType.values()), new Predicate<OType>() {
@Override
public boolean apply(OType input) {
return !input.isMultiValue();
}
}));
}
private static final Predicate<OProperty> CAN_BE_INVERSE_PROPERTY = new Predicate<OProperty>() {
@Override
public boolean apply(OProperty input) {
return input.getType().isLink();
}
};
private static final long serialVersionUID = 1L;
public OPropertyMetaPanel(String id, IModel<DisplayMode> modeModel,
IModel<OProperty> entityModel, IModel<String> propertyModel,
IModel<V> valueModel)
{
super(id, modeModel, entityModel, propertyModel, valueModel);
}
public OPropertyMetaPanel(String id, IModel<DisplayMode> modeModel,
IModel<OProperty> entityModel, IModel<String> propertyModel)
{
super(id, modeModel, entityModel, propertyModel);
}
@SuppressWarnings("unchecked")
@Override
protected V getValue(OProperty entity, String critery) {
CustomAttribute custom;
if(OPropertyPrototyper.COLLATE.equals(critery))
{
OCollate collate = entity.getCollate();
return (V)(collate!=null?collate.getName():null);
}
else if((custom = CustomAttribute.getIfExists(critery))!=null)
{
return custom.getValue(entity);
}
else
{
return (V) PropertyResolver.getValue(critery, entity);
}
}
@Override
protected void setValue(OProperty entity, String critery, V value) {
ODatabaseDocument db = OrientDbWebSession.get().getDatabase();
db.commit();
try
{
CustomAttribute custom;
if(OPropertyPrototyper.COLLATE.equals(critery))
{
entity.setCollate((String)value);
}
else if((custom = CustomAttribute.getIfExists(critery))!=null)
{
custom.setValue(entity, value);
}
else
{
PropertyResolver.setValue(critery, entity, value, null);
}
} finally
{
db.begin();
}
}
@Override
protected void onConfigure() {
super.onConfigure();
String critery = getPropertyObject();
if(OPropertyPrototyper.TYPE.equals(critery))
{
OType oType = (OType)getEnteredValue();
// Show Linked Class if type is a some kind of link
AbstractMetaPanel<OProperty, String, ?> metaPanel = getMetaComponent(OPropertyPrototyper.LINKED_CLASS);
if(metaPanel!=null) metaPanel.setVisibilityAllowed(oType!=null && (oType.isLink() || oType.isEmbedded()));
// Show Linked Type if type is a some kind of embedded
metaPanel = getMetaComponent(OPropertyPrototyper.LINKED_TYPE);
if(metaPanel!=null) metaPanel.setVisibilityAllowed(oType!=null && oType.isEmbedded() && !OType.EMBEDDED.equals(oType));
// Show inverse if current type is a link
metaPanel = getMetaComponent(CustomAttribute.PROP_INVERSE.getName());
if(metaPanel!=null) metaPanel.setVisibilityAllowed(oType!=null && oType.isLink());
}
else if(CustomAttribute.CALCULABLE.getName().equals(critery))
{
Boolean calculable = (Boolean) getEnteredValue();
AbstractMetaPanel<OProperty, String, ?> metaPanel = getMetaComponent(CustomAttribute.CALC_SCRIPT.getName());
if(metaPanel!=null) metaPanel.setVisibilityAllowed(calculable!=null && calculable);
}
}
@SuppressWarnings("unchecked")
@Override
protected Component resolveComponent(String id, DisplayMode mode,
String critery) {
if(DisplayMode.VIEW.equals(mode))
{
if(OPropertyPrototyper.LINKED_CLASS.equals(critery))
{
return new OClassViewPanel(id, (IModel<OClass>)getModel());
}
else if(OPropertyPrototyper.COLLATE.equals(critery))
{
return new Label(id, getModel());
}
else if(CustomAttribute.match(critery, CustomAttribute.PROP_INVERSE)) {
return new OPropertyViewPanel(id, (IModel<OProperty>)getModel());
}
else if(CustomAttribute.match(critery, CustomAttribute.CALC_SCRIPT))
{
return new MultiLineLabel(id, getModel());
}
if(OPropertyPrototyper.MANDATORY.equals(critery)
|| OPropertyPrototyper.READONLY.equals(critery)
|| OPropertyPrototyper.NOT_NULL.equals(critery)
|| CustomAttribute.match(critery, CustomAttribute.UI_READONLY,
CustomAttribute.DISPLAYABLE,
CustomAttribute.CALCULABLE,
CustomAttribute.HIDDEN))
{
return new BooleanViewPanel(id, (IModel<Boolean>)getModel()).setHideIfFalse(true);
}
else
{
return new Label(id, getModel());
}
}
else if(DisplayMode.EDIT.equals(mode))
{
if(OPropertyPrototyper.NAME.equals(critery))
{
return new TextField<V>(id, getModel()).setType(String.class).setRequired(true)
.add((IValidator<V>)OSchemaNamesValidator.FIELD_NAME_VALIDATOR).setRequired(true);
}
else if(OPropertyPrototyper.TYPE.equals(critery))
{
return new DropDownChoice<OType>(id, (IModel<OType>)getModel(), new ListAvailableOTypesModel(getEntityModel()))
.setRequired(true)
.add(new RefreshMetaContextOnChangeBehaviour());
}
else if(OPropertyPrototyper.LINKED_TYPE.equals(critery))
{
return new DropDownChoice<OType>(id, (IModel<OType>)getModel(), LINKED_TYPE_OPTIONS).setNullValid(true);
}
else if(OPropertyPrototyper.LINKED_CLASS.equals(critery))
{
return new DropDownChoice<OClass>(id, (IModel<OClass>)getModel(), new ListOClassesModel(), OClassChoiceRenderer.INSTANCE)
.setNullValid(true).add(new RefreshMetaContextOnChangeBehaviour());
}
else if(OPropertyPrototyper.COLLATE.equals(critery))
{
return new DropDownChoice<String>(id, (IModel<String>)getModel(), Lists.newArrayList(OSQLEngine.getCollateNames()));
}
else if(OPropertyPrototyper.MANDATORY.equals(critery)
|| OPropertyPrototyper.READONLY.equals(critery)
|| OPropertyPrototyper.NOT_NULL.equals(critery))
{
return new CheckBox(id, (IModel<Boolean>)getModel());
}
else if(OPropertyPrototyper.MIN.equals(critery)
|| OPropertyPrototyper.MAX.equals(critery)
|| OPropertyPrototyper.REGEXP.equals(critery)
|| OPropertyPrototyper.DEFAULT_VALUE.equals(critery))
{
return new TextField<V>(id, getModel());
}
else
{
final CustomAttribute customAttr = CustomAttribute.getIfExists(critery);
if(customAttr!=null)
{
if(customAttr.equals(CustomAttribute.CALCULABLE)) {
return new CheckBox(id, (IModel<Boolean>)getModel()).add(new RefreshMetaContextOnChangeBehaviour());
} else if(customAttr.matchAny(CustomAttribute.DISPLAYABLE, CustomAttribute.HIDDEN, CustomAttribute.UI_READONLY)) {
return new CheckBox(id, (IModel<Boolean>)getModel());
} else if(customAttr.matchAny(CustomAttribute.CALC_SCRIPT, CustomAttribute.DESCRIPTION)) {
return new TextArea<V>(id, getModel());
} else if(customAttr.equals(CustomAttribute.ORDER)) {
return new TextField<V>(id, getModel()).setType(Integer.class);
} else if(customAttr.equals(CustomAttribute.TAB)) {
return new TextField<V>(id, getModel());
} else if(customAttr.equals(CustomAttribute.VISUALIZATION_TYPE)) {
return new DropDownChoice<String>(id, (IModel<String>)getModel(), new LoadableDetachableModel<List<String>>() {
@Override
protected List<String> load() {
OType type = getMetaComponentEnteredValue(OPropertyPrototyper.TYPE);
UIVisualizersRegistry registry = OrienteerWebApplication.get().getUIVisualizersRegistry();
return registry.getComponentsOptions(type);
}
})
{
private static final long serialVersionUID = 1L;
@Override
protected void onConfigure() {
super.onConfigure();
List<?> choices = getChoices();
setVisible(choices!=null && choices.size()>0);
}
}.setNullValid(false).setRequired(true);
} else if(customAttr.equals(CustomAttribute.PROP_INVERSE)) {
IModel<OClass> linkedClassModel = (IModel<OClass>)getMetaComponentEnteredValueModel(OPropertyPrototyper.LINKED_CLASS);
return new DropDownChoice<OProperty>(id, (IModel<OProperty>)getModel(),
new ListOPropertiesModel(linkedClassModel, null)
{
@Override
protected Predicate<? super OProperty> getFilterPredicate() {
return CAN_BE_INVERSE_PROPERTY;
}
}
).setNullValid(true);
}
}
return resolveComponent(id, DisplayMode.VIEW, critery);
}
}
else return null;
}
@Override
public IModel<String> newLabelModel() {
return new SimpleNamingModel<String>("property", getPropertyModel());
}
}