/* This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2013 Servoy BV This program is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program; if not, see http://www.gnu.org/licenses or write to the Free Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 */ package com.servoy.j2db.server.ngclient.component; import java.awt.Dimension; import java.awt.Point; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.mozilla.javascript.BaseFunction; import org.mozilla.javascript.Callable; import org.mozilla.javascript.Context; import org.mozilla.javascript.Scriptable; import org.sablo.WebComponent; import org.sablo.specification.PropertyDescription; import org.sablo.specification.WebObjectSpecification; import com.servoy.j2db.component.ComponentFactory; import com.servoy.j2db.persistence.AbstractBase; import com.servoy.j2db.persistence.Field; import com.servoy.j2db.persistence.Form; import com.servoy.j2db.persistence.GraphicalComponent; import com.servoy.j2db.persistence.IFormElement; import com.servoy.j2db.persistence.IPersist; import com.servoy.j2db.persistence.ISupportName; import com.servoy.j2db.persistence.Part; import com.servoy.j2db.persistence.Portal; import com.servoy.j2db.persistence.RectShape; import com.servoy.j2db.persistence.StaticContentSpecLoader; import com.servoy.j2db.persistence.TabPanel; import com.servoy.j2db.scripting.IInstanceOf; import com.servoy.j2db.server.ngclient.WebFormComponent; import com.servoy.j2db.server.ngclient.property.types.NGConversions; import com.servoy.j2db.server.ngclient.property.types.NGConversions.ISabloComponentToRhino; import com.servoy.j2db.server.ngclient.property.types.ValueListTypeSabloValue; import com.servoy.j2db.server.ngclient.template.FormTemplateGenerator; import com.servoy.j2db.ui.runtime.IRuntimeComponent; import com.servoy.j2db.util.Utils; /** * Runtime component for legacy scripting methods from default Servoy components. * * @author lvostinar * */ @SuppressWarnings("nls") public class RuntimeLegacyComponent implements Scriptable, IInstanceOf { private final WebFormComponent component; private final PutPropertyCallable putCallable; private final GetPropertyCallable getCallable; private final static Map<String, String> ScriptNameToSpecName; private final static Set<String> LegacyApiNames; private Map<Object, Object> clientProperties; private final WebObjectSpecification webComponentSpec; private Scriptable parentScope; static { ScriptNameToSpecName = new HashMap<String, String>(); ScriptNameToSpecName.put("bgcolor", StaticContentSpecLoader.PROPERTY_BACKGROUND.getPropertyName()); ScriptNameToSpecName.put("fgcolor", StaticContentSpecLoader.PROPERTY_FOREGROUND.getPropertyName()); ScriptNameToSpecName.put("width", StaticContentSpecLoader.PROPERTY_SIZE.getPropertyName()); ScriptNameToSpecName.put("height", StaticContentSpecLoader.PROPERTY_SIZE.getPropertyName()); ScriptNameToSpecName.put("locationX", StaticContentSpecLoader.PROPERTY_LOCATION.getPropertyName()); ScriptNameToSpecName.put("locationY", StaticContentSpecLoader.PROPERTY_LOCATION.getPropertyName()); ScriptNameToSpecName.put("border", StaticContentSpecLoader.PROPERTY_BORDERTYPE.getPropertyName()); ScriptNameToSpecName.put("font", StaticContentSpecLoader.PROPERTY_FONTTYPE.getPropertyName()); ScriptNameToSpecName.put("imageURL", StaticContentSpecLoader.PROPERTY_IMAGEMEDIAID.getPropertyName()); ScriptNameToSpecName.put("rolloverImageURL", StaticContentSpecLoader.PROPERTY_ROLLOVERIMAGEMEDIAID.getPropertyName()); ScriptNameToSpecName.put("valueListItems", StaticContentSpecLoader.PROPERTY_VALUELISTID.getPropertyName()); ScriptNameToSpecName.put("valueListName", StaticContentSpecLoader.PROPERTY_VALUELISTID.getPropertyName()); LegacyApiNames = new HashSet<>(); LegacyApiNames.add("putClientProperty"); LegacyApiNames.add("getLabelForElementNames"); LegacyApiNames.add("getLabelForElementName"); LegacyApiNames.add("getElementType"); LegacyApiNames.add("getName"); LegacyApiNames.add("getValueListName"); LegacyApiNames.add("getDesignTimeProperty"); LegacyApiNames.add("getLocationX"); LegacyApiNames.add("getLocationY"); LegacyApiNames.add("getWidth"); LegacyApiNames.add("getHeight"); LegacyApiNames.add("getDataProviderID"); } public RuntimeLegacyComponent(WebFormComponent component) { this.component = component; setParentScope(component.getDataConverterContext().getApplication().getScriptEngine().getSolutionScope()); putCallable = new PutPropertyCallable(this); getCallable = new GetPropertyCallable(this); this.webComponentSpec = component.getFormElement().getWebComponentSpec(); } @Override public boolean isInstance(String name) { return name.equals(getRuntimeName()); } private String getRuntimeName() { IPersist persist = component.getFormElement().getPersistIfAvailable(); if (persist instanceof GraphicalComponent) { boolean noData = ((GraphicalComponent)persist).getDataProviderID() == null && !((GraphicalComponent)persist).getDisplaysTags(); if (com.servoy.j2db.component.ComponentFactory.isButton((GraphicalComponent)persist)) { return noData ? "RuntimeButton" : "RuntimeDataButton"; } return noData ? "RuntimeLabel" : "RuntimeDataLabel"; } if (persist instanceof Field) { switch (((Field)persist).getDisplayType()) { case Field.COMBOBOX : return "RuntimeComboBox"; case Field.TEXT_FIELD : case Field.TYPE_AHEAD : return "RuntimeTextField"; case Field.RADIOS : return FormTemplateGenerator.isSingleValueComponent((IFormElement)persist) ? "RuntimeRadio" : "RuntimeRadios"; case Field.CHECKS : return FormTemplateGenerator.isSingleValueComponent((IFormElement)persist) ? "RuntimeCheck" : "RuntimeChecks"; case Field.CALENDAR : return "RuntimeCalendar"; case Field.TEXT_AREA : return "RuntimeTextArea"; case Field.PASSWORD : return "RuntimePassword"; case Field.SPINNER : return "RuntimeSpinner"; case Field.LIST_BOX : case Field.MULTISELECT_LISTBOX : return ";RuntimeListBox"; case Field.IMAGE_MEDIA : return "RuntimeMediaField"; case Field.HTML_AREA : return "RuntimeHtmlArea"; } } if (persist instanceof TabPanel) { int orient = ((TabPanel)persist).getTabOrientation(); if (orient == TabPanel.SPLIT_HORIZONTAL || orient == TabPanel.SPLIT_VERTICAL) return "RuntimeSplitPane"; if (orient == TabPanel.ACCORDION_PANEL) return "RuntimeAccordionPanel"; return "RuntimeTabPanel"; } if (persist instanceof Portal) { return "RuntimePortal"; } return null; } @Override public String getClassName() { return null; } @Override public Object get(String name, Scriptable start) { if ("putClientProperty".equals(name)) { putCallable.setProperty("clientProperty"); return putCallable; } if ("addStyleClass".equals(name) || "removeStyleClass".equals(name)) { final String nameFinal = name; return new Callable() { @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (webComponentSpec.getProperty(StaticContentSpecLoader.PROPERTY_STYLECLASS.getPropertyName()) != null) { String styleClass = (String)component.getProperty(StaticContentSpecLoader.PROPERTY_STYLECLASS.getPropertyName()); if (args != null && args.length > 0 && args[0] != null) { if (styleClass == null) styleClass = ""; if ("addStyleClass".equals(nameFinal)) { styleClass = styleClass + " " + args[0]; } else { styleClass = styleClass.replaceAll(args[0].toString(), ""); } component.setProperty(StaticContentSpecLoader.PROPERTY_STYLECLASS.getPropertyName(), styleClass); } } return null; } }; } if (name.startsWith("get") || name.startsWith("is") || name.startsWith("set")) { String newName = generatePropertyName(name); if (name.startsWith("set")) { putCallable.setProperty(newName); return putCallable; } else { getCallable.setProperty(newName); return getCallable; } } boolean isReadonly = false; if (name.equals("readOnly")) { isReadonly = true; name = StaticContentSpecLoader.PROPERTY_EDITABLE.getPropertyName(); } if (component.isDesignOnlyProperty(name) || component.isPrivateProperty(name)) { // cannot get design only or private properties return Scriptable.NOT_FOUND; } Object value = convertValue(name, component.getProperty(convertName(name)), webComponentSpec.getProperties().get(convertName(name)), start); if (isReadonly && value instanceof Boolean) { return !((Boolean)value).booleanValue(); } return value; } /** * @param name * @return */ protected String generatePropertyName(String name) { String newName = name.substring(name.startsWith("is") ? 2 : 3); // Make the bean property name. char ch0 = newName.charAt(0); if (Character.isUpperCase(ch0)) { if (newName.length() == 1) { newName = newName.toLowerCase(); } else { char ch1 = newName.charAt(1); if (!Character.isUpperCase(ch1)) { newName = Character.toLowerCase(ch0) + newName.substring(1); } } } return newName; } @Override public Object get(int index, Scriptable start) { return null; } @Override public boolean has(String name, Scriptable start) { if (component.isDesignOnlyProperty(name) || component.isPrivateProperty(name)) return false; if (name.equals("readOnly") || LegacyApiNames.contains(name) || ScriptNameToSpecName.containsKey(name)) return true; if (webComponentSpec.getApiFunction(name) != null) return true; if (webComponentSpec.getProperty(name) != null) return true; if (name.startsWith("get") || name.startsWith("is") || name.startsWith("set")) { String newName = generatePropertyName(name); if (webComponentSpec.getProperty(newName) != null) return true; } if ("addStyleClass".equals(name) || "removeStyleClass".equals(name)) { if (webComponentSpec.getProperty(StaticContentSpecLoader.PROPERTY_STYLECLASS.getPropertyName()) != null) return true; } return false; } @Override public boolean has(int index, Scriptable start) { return false; } @Override public void put(String name, Scriptable start, Object value) { if (name.equals("readOnly")) { name = StaticContentSpecLoader.PROPERTY_EDITABLE.getPropertyName(); if (value instanceof Boolean) { value = !((Boolean)value).booleanValue(); } } name = convertName(name); if (component.isDesignOnlyProperty(name) && !StaticContentSpecLoader.PROPERTY_VALUELISTID.getPropertyName().equals(name)) { // cannot set design only or private properties return; } Object previousVal = component.getProperty(name); Object val = NGConversions.INSTANCE.convertRhinoToSabloComponentValue(value, previousVal, webComponentSpec.getProperties().get(name), component); if (val != previousVal) component.setProperty(name, val); // force size & location push as that maybe different on the client (if form anchored or table columns were changed as width or location) if ("size".equals(name) || "location".equals(name)) { component.flagPropertyAsDirty(name, true); } } @Override public void put(int index, Scriptable start, Object value) { } @Override public void delete(String name) { // TODO Auto-generated method stub } @Override public void delete(int index) { } @Override public Scriptable getPrototype() { return null; } @Override public void setPrototype(Scriptable prototype) { } @Override public Scriptable getParentScope() { return parentScope; } @Override public void setParentScope(Scriptable parent) { parentScope = parent; } @Override public Object[] getIds() { return LegacyApiNames.toArray(); } @Override public Object getDefaultValue(Class< ? > hint) { return null; } @Override public boolean hasInstance(Scriptable instance) { return false; } private String convertName(String name) { if (ScriptNameToSpecName.containsKey(name)) { return ScriptNameToSpecName.get(name); } return name; } private Object convertValue(String name, Object value, PropertyDescription pd, Scriptable start) { if ("width".equals(name) && value instanceof Dimension) { return Integer.valueOf(((Dimension)value).width); } if ("height".equals(name) && value instanceof Dimension) { return Integer.valueOf(((Dimension)value).height); } if ("locationX".equals(name) && value instanceof Point) { return Integer.valueOf(((Point)value).x); } if ("locationY".equals(name) && value instanceof Point) { return Integer.valueOf(((Point)value).y); } if (pd != null && pd.getType() instanceof ISabloComponentToRhino< ? >) { return ((ISabloComponentToRhino)pd.getType()).toRhinoValue(value, pd, component, start); } return value; } private boolean needsValueConversion(String name) { if ("width".equals(name) || "height".equals(name) || "locationX".equals(name) || "locationY".equals(name)) { return true; } return false; } public void putClientProperty(Object key, Object value) { if (clientProperties == null) { clientProperties = new HashMap<Object, Object>(); } clientProperties.put(key, value); } public Object getClientProperty(Object key) { if (clientProperties == null) return null; return clientProperties.get(key); } private abstract class PropertyCallable extends BaseFunction { protected final Scriptable scriptable; protected String propertyName; public PropertyCallable(Scriptable scriptable) { this.scriptable = scriptable; } public void setProperty(String propertyName) { this.propertyName = propertyName; } } private class PutPropertyCallable extends PropertyCallable { public PutPropertyCallable(Scriptable scriptable) { super(scriptable); } @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { Object value = args; if (args != null && args.length == 1) { value = args[0]; } if ("clientProperty".equals(propertyName) && args != null && args.length >= 2) { putClientProperty(args[0], args[1]); } // if (StaticContentSpecLoader.PROPERTY_LOCATION.getPropertyName().equals(propertyName)) // { // scriptable.put(StaticContentSpecLoader.PROPERTY_ANCHORS.getPropertyName(), null, Integer.valueOf(IAnchorConstants.DEFAULT)); // } scriptable.put(propertyName, null, value); return null; } } private class GetPropertyCallable extends PropertyCallable { public GetPropertyCallable(Scriptable scriptable) { super(scriptable); } @Override public Object call(Context cx, Scriptable scope, Scriptable thisObj, Object[] args) { if (propertyName.equals("absoluteFormLocationY") && component.getFormElement().getPersistIfAvailable() instanceof IFormElement) //$NON-NLS-1$ { int y = ((Integer)scriptable.get("locationY", null)).intValue(); IFormElement fe = (IFormElement)component.getFormElement().getPersistIfAvailable(); Form f = component.getFormElement().getForm(); Part p = f.getPartAt(fe.getLocation().y); int partStartY = 0; if (p != null) { partStartY = f.getPartStartYPos(p.getID()); } return Integer.valueOf(partStartY + y); } if ("clientProperty".equals(propertyName) && args != null && args.length > 0) { return getClientProperty(args[0]); } if ("dataProviderID".equals(propertyName)) { // return the design value string (so the dataprovider name, not its value) return component.getFormElement().getPropertyValue(propertyName); } if ("labelForElementName".equals(propertyName)) { if (component.getFormElement().getPersistIfAvailable() instanceof GraphicalComponent) { GraphicalComponent gc = (GraphicalComponent)component.getFormElement().getPersistIfAvailable(); String labelFor = gc.getLabelFor(); if (labelFor != null) { WebComponent comp = component.getParent().getComponent(labelFor); if (comp != null) { return comp.getName(); } } } return null; } if ("labelForElementNames".equals(propertyName)) { if (component.getFormElement().getPersistIfAvailable() instanceof Field) { Field field = (Field)component.getFormElement().getPersistIfAvailable(); String name = field.getName(); if (name != null) { List<String> labelFors = new ArrayList<String>(); for (WebComponent comp : component.getParent().getComponents()) { if (comp instanceof WebFormComponent) { WebFormComponent c = (WebFormComponent)comp; if (c.getFormElement().getPersistIfAvailable() instanceof GraphicalComponent && Utils.equalObjects(name, ((GraphicalComponent)c.getFormElement().getPersistIfAvailable()).getLabelFor()) && ((GraphicalComponent)c.getFormElement().getPersistIfAvailable()).getName() != null) { labelFors.add(c.getName()); } } } return labelFors.toArray(new String[0]); } } return new String[0]; } if ("elementType".equals(propertyName)) { // return the design value string (so the dataprovider name, not its value) IPersist persist = component.getFormElement().getPersistIfAvailable(); if (persist instanceof GraphicalComponent) { if (com.servoy.j2db.component.ComponentFactory.isButton((GraphicalComponent)persist)) { return IRuntimeComponent.BUTTON; } return IRuntimeComponent.LABEL; } if (persist instanceof Field) { switch (((Field)persist).getDisplayType()) { case Field.COMBOBOX : return IRuntimeComponent.COMBOBOX; case Field.TEXT_FIELD : return IRuntimeComponent.TEXT_FIELD; case Field.RADIOS : return IRuntimeComponent.RADIOS; case Field.CHECKS : return IRuntimeComponent.CHECK; case Field.CALENDAR : return IRuntimeComponent.CALENDAR; case Field.TYPE_AHEAD : return IRuntimeComponent.TYPE_AHEAD; case Field.TEXT_AREA : return IRuntimeComponent.TEXT_AREA; case Field.PASSWORD : return IRuntimeComponent.PASSWORD; case Field.SPINNER : return IRuntimeComponent.SPINNER; case Field.LIST_BOX : return IRuntimeComponent.LISTBOX; case Field.MULTISELECT_LISTBOX : return IRuntimeComponent.MULTISELECT_LISTBOX; case Field.IMAGE_MEDIA : return IRuntimeComponent.IMAGE_MEDIA; case Field.HTML_AREA : return IRuntimeComponent.HTML_AREA; } } if (persist instanceof TabPanel) { int orient = ((TabPanel)persist).getTabOrientation(); if (orient == TabPanel.SPLIT_HORIZONTAL || orient == TabPanel.SPLIT_VERTICAL) return IRuntimeComponent.SPLITPANE; if (orient == TabPanel.ACCORDION_PANEL) return IRuntimeComponent.ACCORDIONPANEL; return IRuntimeComponent.TABPANEL; } if (persist instanceof Portal) { return IRuntimeComponent.PORTAL; } if (persist instanceof RectShape) { return IRuntimeComponent.RECTANGLE; } } if ("name".equals(propertyName)) { IPersist persist = component.getFormElement().getPersistIfAvailable(); if (persist instanceof ISupportName) { String jsName = ((ISupportName)persist).getName(); if (jsName != null && jsName.startsWith(ComponentFactory.WEB_ID_PREFIX)) jsName = null; return jsName; } return null; } if (propertyName.equals("designTimeProperty") && args != null && args.length > 0 && component.getFormElement().getPersistIfAvailable() instanceof AbstractBase) { return Utils.parseJSExpression(((AbstractBase)component.getFormElement().getPersistIfAvailable()).getCustomDesignTimeProperty((String)args[0])); } if ("valueListName".equals(propertyName)) { Object vl = component.getProperty(convertName(propertyName)); if (vl != null) { ValueListTypeSabloValue value = (ValueListTypeSabloValue)vl; if (value.getValueList() != null) return value.getValueList().getName(); } } return scriptable.get(propertyName, null); } } }