/*
* Copyright (C) 2014 Servoy BV
*
* 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 com.servoy.j2db.server.ngclient.property.types;
import java.awt.Color;
import java.awt.Font;
import java.awt.Insets;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import javax.swing.BorderFactory;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;
import javax.swing.border.LineBorder;
import javax.swing.border.MatteBorder;
import javax.swing.border.TitledBorder;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONStringer;
import org.json.JSONWriter;
import org.mozilla.javascript.Scriptable;
import org.sablo.BaseWebObject;
import org.sablo.specification.PropertyDescription;
import org.sablo.specification.property.IBrowserConverterContext;
import org.sablo.specification.property.IConvertedPropertyType;
import org.sablo.specification.property.types.DefaultPropertyType;
import org.sablo.specification.property.types.FontPropertyType;
import org.sablo.util.ValueReference;
import org.sablo.websocket.utils.DataConversion;
import org.sablo.websocket.utils.JSONUtils;
import com.servoy.j2db.FlattenedSolution;
import com.servoy.j2db.IApplication;
import com.servoy.j2db.persistence.IDesignValueConverter;
import com.servoy.j2db.scripting.solutionmodel.JSWebComponent;
import com.servoy.j2db.server.ngclient.FormElementContext;
import com.servoy.j2db.server.ngclient.INGFormElement;
import com.servoy.j2db.server.ngclient.property.types.NGConversions.IDesignToFormElement;
import com.servoy.j2db.server.ngclient.property.types.NGConversions.IFormElementToTemplateJSON;
import com.servoy.j2db.server.ngclient.property.types.NGConversions.IRhinoToSabloComponent;
import com.servoy.j2db.server.ngclient.property.types.NGConversions.ISabloComponentToRhino;
import com.servoy.j2db.util.ComponentFactoryHelper;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.IRhinoDesignConverter;
import com.servoy.j2db.util.PersistHelper;
import com.servoy.j2db.util.Utils;
import com.servoy.j2db.util.gui.RoundedBorder;
import com.servoy.j2db.util.gui.SpecialMatteBorder;
/**
* @author jcompagner
*
*/
public class BorderPropertyType extends DefaultPropertyType<Border>
implements IConvertedPropertyType<Border>, IDesignToFormElement<JSONObject, Border, Border>, IFormElementToTemplateJSON<Border, Border>,
IRhinoToSabloComponent<Border>, ISabloComponentToRhino<Border>, IRhinoDesignConverter, IDesignValueConverter<Border>
{
private static final String TYPE = "type";
private static final String BORDER_RADIUS = "borderRadius";
private static final String TITLE_POSITION = "titlePosition";
private static final String TITLE_JUSTIFICATION = "titleJustification";
private static final String TITLE = "title";
private static final String BORDER_RIGHT_COLOR = "borderRightColor";
private static final String BORDER_LEFT_COLOR = "borderLeftColor";
private static final String BORDER_BOTTOM_COLOR = "borderBottomColor";
private static final String BORDER_TOP_COLOR = "borderTopColor";
private static final String BORDER_RIGHT_WIDTH = "borderRightWidth";
private static final String BORDER_BOTTOM_WIDTH = "borderBottomWidth";
private static final String BORDER_LEFT_WIDTH = "borderLeftWidth";
private static final String BORDER_TOP_WIDTH = "borderTopWidth";
private static final String BORDER_COLOR = "borderColor";
private static final String BORDER_WIDTH = "borderWidth";
private static final String BORDER_STYLE = "borderStyle";
public static final BorderPropertyType INSTANCE = new BorderPropertyType();
public static final String TYPE_NAME = "border";
private BorderPropertyType()
{
}
@Override
public String getName()
{
return TYPE_NAME;
}
@Override
public Border fromJSON(Object newValue, Border previousValue, PropertyDescription pd, IBrowserConverterContext dataConverterContext,
ValueReference<Boolean> returnValueAdjustedIncommingValue)
{
if (newValue == null) return null;
JSONObject object = (JSONObject)newValue;
String type = object.optString(TYPE);
if (type == null) return null;
JSONObject borderStyle = object.optJSONObject(BORDER_STYLE);
switch (type)
{
case ComponentFactoryHelper.BEVEL_BORDER :
{
int borderType = "outset".equals(borderStyle.optString(BORDER_STYLE)) ? BevelBorder.RAISED : BevelBorder.LOWERED;
String borderColor = borderStyle.optString(BORDER_COLOR);
StringTokenizer st = new StringTokenizer(borderColor, " ");
Color hiOut = null;
Color hiin = null;
Color shOut = null;
Color shIn = null;
if (st.hasMoreTokens()) hiOut = PersistHelper.createColor(st.nextToken());
if (st.hasMoreTokens()) shOut = PersistHelper.createColor(st.nextToken());
if (st.hasMoreTokens()) shIn = PersistHelper.createColor(st.nextToken());
if (st.hasMoreTokens()) hiin = PersistHelper.createColor(st.nextToken());
return BorderFactory.createBevelBorder(borderType, hiOut, hiin, shOut, shIn);
}
case ComponentFactoryHelper.EMPTY_BORDER :
{
Insets insets = PersistHelper.createInsets(borderStyle.optString(BORDER_WIDTH).replaceAll("px ", ","));
return BorderFactory.createEmptyBorder(insets.top, insets.right, insets.bottom, insets.left);
}
case ComponentFactoryHelper.ETCHED_BORDER :
{
StringTokenizer st = new StringTokenizer(borderStyle.optString(BORDER_COLOR), " ");
Color hi = null;
Color sh = null;
if (st.hasMoreTokens()) hi = PersistHelper.createColor(st.nextToken());
if (st.hasMoreTokens()) sh = PersistHelper.createColor(st.nextToken());
int t = borderStyle.optString(BORDER_STYLE).equals("ridge") ? EtchedBorder.RAISED : EtchedBorder.LOWERED;
return BorderFactory.createEtchedBorder(t, hi, sh);
}
case ComponentFactoryHelper.LINE_BORDER :
{
Color borderColor = PersistHelper.createColor(borderStyle.optString(BORDER_COLOR));
String width = borderStyle.optString(BORDER_WIDTH);
if (width != null) width = width.substring(0, width.length() - 2);
return BorderFactory.createLineBorder(borderColor, Utils.getAsInteger(width));
}
case ComponentFactoryHelper.MATTE_BORDER :
{
Insets insets = PersistHelper.createInsets(borderStyle.optString(BORDER_WIDTH).replaceAll("px ", ","));
Color borderColor = PersistHelper.createColor(borderStyle.optString(BORDER_COLOR));
return BorderFactory.createMatteBorder(insets.top, insets.right, insets.bottom, insets.left, borderColor);
}
case ComponentFactoryHelper.ROUNDED_BORDER :
{
float top = Utils.getAsFloat(borderStyle.optString(BORDER_TOP_WIDTH).replace("px", ""));
float left = Utils.getAsFloat(borderStyle.optString(BORDER_LEFT_WIDTH).replace("px", ""));
float bottom = Utils.getAsFloat(borderStyle.optString(BORDER_BOTTOM_WIDTH).replace("px", ""));
float right = Utils.getAsFloat(borderStyle.optString(BORDER_RIGHT_WIDTH).replace("px", ""));
Color topColor = PersistHelper.createColor(borderStyle.optString(BORDER_TOP_COLOR));
Color bottomColor = PersistHelper.createColor(borderStyle.optString(BORDER_BOTTOM_COLOR));
Color leftColor = PersistHelper.createColor(borderStyle.optString(BORDER_LEFT_COLOR));
Color rightColor = PersistHelper.createColor(borderStyle.optString(BORDER_RIGHT_COLOR));
RoundedBorder border = new RoundedBorder(top, left, bottom, right, topColor, leftColor, bottomColor, rightColor);
border.setBorderStyles(borderStyle.optString(BORDER_STYLE).replaceAll(" ", ";"));
border.setRoundingRadius(borderStyle.optString(BORDER_RADIUS).replaceAll("px", ";").replaceAll(" ", "").replaceAll("/", ""));
return border;
}
case ComponentFactoryHelper.SPECIAL_MATTE_BORDER :
{
float top = Utils.getAsFloat(borderStyle.optString(BORDER_TOP_WIDTH).replace("px", ""));
float left = Utils.getAsFloat(borderStyle.optString(BORDER_LEFT_WIDTH).replace("px", ""));
float bottom = Utils.getAsFloat(borderStyle.optString(BORDER_BOTTOM_WIDTH).replace("px", ""));
float right = Utils.getAsFloat(borderStyle.optString(BORDER_RIGHT_WIDTH).replace("px", ""));
Color topColor = PersistHelper.createColor(borderStyle.optString(BORDER_TOP_COLOR));
Color bottomColor = PersistHelper.createColor(borderStyle.optString(BORDER_BOTTOM_COLOR));
Color leftColor = PersistHelper.createColor(borderStyle.optString(BORDER_LEFT_COLOR));
Color rightColor = PersistHelper.createColor(borderStyle.optString(BORDER_RIGHT_COLOR));
return new SpecialMatteBorder(top, left, bottom, right, topColor, leftColor, bottomColor, rightColor);
}
case ComponentFactoryHelper.TITLED_BORDER :
{
String borderTitle = object.optString(TITLE);
int titleJustification = TitledBorder.DEFAULT_JUSTIFICATION;
switch (object.optString(TITLE_JUSTIFICATION))
{
case "left" :
titleJustification = TitledBorder.LEFT;
break;
case "right" :
titleJustification = TitledBorder.RIGHT;
break;
case "center" :
titleJustification = TitledBorder.CENTER;
break;
case "leading" :
titleJustification = TitledBorder.LEADING;
break;
case "trailing" :
titleJustification = TitledBorder.TRAILING;
}
int titlePosition = TitledBorder.DEFAULT_POSITION;
switch (object.optString(TITLE_POSITION))
{
case "Above top" :
titlePosition = TitledBorder.ABOVE_TOP;
break;
case "Top" :
titlePosition = TitledBorder.TOP;
break;
case "Below top" :
titlePosition = TitledBorder.BELOW_TOP;
break;
case "Above bottom" :
titlePosition = TitledBorder.ABOVE_BOTTOM;
break;
case "Below bottom" :
titlePosition = TitledBorder.BELOW_BOTTOM;
break;
case "Bottom" :
titlePosition = TitledBorder.BOTTOM;
break;
}
Font titleFont = FontPropertyType.INSTANCE.fromJSON(object.optJSONObject("font"), null, null, dataConverterContext, null);//TODO previous value
Color titleColor = PersistHelper.createColor(object.optString("color"));
return BorderFactory.createTitledBorder(null, borderTitle, titleJustification, titlePosition, titleFont, titleColor);
}
}
return null;
}
@Override
public JSONWriter toJSON(JSONWriter writer, String key, Border value, PropertyDescription pd, DataConversion clientConversion,
IBrowserConverterContext dataConverterContext) throws JSONException
{
Map<String, Object> javaResult = writeBorderToJson(value);
return JSONUtils.toBrowserJSONFullValue(writer, key, javaResult, null, clientConversion, null);
}
public static Map<String, Object> writeBorderToJson(Border value)
{
Map<String, Object> map = new HashMap<>();
if (value instanceof SpecialMatteBorder)
{
SpecialMatteBorder border = (SpecialMatteBorder)value;
map.put(TYPE, ((border instanceof RoundedBorder) ? ComponentFactoryHelper.ROUNDED_BORDER : ComponentFactoryHelper.SPECIAL_MATTE_BORDER));
Map<String, Object> borderStyle = new HashMap<>();
map.put(BORDER_STYLE, borderStyle);
borderStyle.put(BORDER_TOP_WIDTH, border.getTop() + "px");
borderStyle.put(BORDER_RIGHT_WIDTH, border.getRight() + "px");
borderStyle.put(BORDER_BOTTOM_WIDTH, border.getBottom() + "px");
borderStyle.put(BORDER_LEFT_WIDTH, border.getLeft() + "px");
borderStyle.put(BORDER_TOP_COLOR, border.getTopColor());
borderStyle.put(BORDER_RIGHT_COLOR, border.getRightColor());
borderStyle.put(BORDER_BOTTOM_COLOR, border.getBottomColor());
borderStyle.put(BORDER_LEFT_COLOR, border.getLeftColor());
if (border instanceof RoundedBorder)
{
float[] radius = ((RoundedBorder)border).getRadius();
borderStyle.put(BORDER_RADIUS, radius[0] + "px " + radius[2] + "px " + radius[4] + "px " + radius[6] + "px /" + radius[1] + "px " + radius[3] +
"px " + radius[5] + "px " + radius[7] + "px");
String styles[] = ((RoundedBorder)border).getBorderStyles();
borderStyle.put(BORDER_STYLE, styles[0] + " " + styles[1] + " " + styles[2] + " " + styles[3]);
}
else
{
borderStyle.put(BORDER_RADIUS, border.getRoundingRadius() + "px"); //$NON-NLS-1$
//retval += "," + SpecialMatteBorder.createDashString(border.getDashPattern()); //$NON-NLS-1$
if (border.getDashPattern() != null)
{
borderStyle.put(BORDER_STYLE, "dashed");
}
else
{
borderStyle.put(BORDER_STYLE, "solid");
}
}
}
else if (value instanceof EtchedBorder)
{
EtchedBorder border = (EtchedBorder)value;
map.put(TYPE, ComponentFactoryHelper.ETCHED_BORDER);
Map<String, Object> borderStyle = new HashMap<>();
map.put(BORDER_STYLE, borderStyle);
String hi = PersistHelper.createColorString(border.getHighlightColor());
String sh = PersistHelper.createColorString(border.getShadowColor());
if (border.getEtchType() != EtchedBorder.RAISED)
{
String tmp = hi;
hi = sh;
sh = tmp;
}
borderStyle.put(BORDER_COLOR, hi + " " + sh + " " + sh + " " + hi);
borderStyle.put(BORDER_STYLE, border.getEtchType() == EtchedBorder.RAISED ? "ridge" : "groove");
borderStyle.put(BORDER_WIDTH, "2px");
}
else if (value instanceof BevelBorder)
{
BevelBorder border = (BevelBorder)value;
map.put(TYPE, ComponentFactoryHelper.BEVEL_BORDER);
Map<String, Object> borderStyle = new HashMap<>();
map.put(BORDER_STYLE, borderStyle);
borderStyle.put(BORDER_STYLE, border.getBevelType() == BevelBorder.RAISED ? "outset" : "inset");
String hiOut = PersistHelper.createColorString(border.getHighlightOuterColor());
String hiin = PersistHelper.createColorString(border.getHighlightInnerColor());
String shOut = PersistHelper.createColorString(border.getShadowOuterColor());
String shIn = PersistHelper.createColorString(border.getShadowInnerColor());
if (border.getBevelType() == BevelBorder.LOWERED)
{
String temp = hiOut; // swap 1-3
hiOut = shOut;
shOut = temp;
temp = hiin; // swap 2-4
hiin = shIn;
shIn = temp;
}
borderStyle.put(BORDER_COLOR, hiOut + " " + shOut + " " + shIn + " " + hiin);
borderStyle.put(BORDER_WIDTH, "2px");
}
else if (value instanceof LineBorder)
{
LineBorder border = (LineBorder)value;
map.put(TYPE, ComponentFactoryHelper.LINE_BORDER);
Map<String, Object> borderStyle = new HashMap<>();
map.put(BORDER_STYLE, borderStyle);
int thick = border.getThickness();
borderStyle.put(BORDER_COLOR, border.getLineColor());
borderStyle.put(BORDER_STYLE, "solid");
borderStyle.put(BORDER_WIDTH, thick + "px");
}
else if (value instanceof MatteBorder)
{
MatteBorder border = (MatteBorder)value;
map.put(TYPE, ComponentFactoryHelper.MATTE_BORDER);
Map<String, Object> borderStyle = new HashMap<>();
map.put(BORDER_STYLE, borderStyle);
Insets in = border.getBorderInsets();
borderStyle.put(BORDER_WIDTH, in.top + "px " + in.right + "px " + in.bottom + "px " + in.left + "px ");
borderStyle.put(BORDER_COLOR, border.getMatteColor());
borderStyle.put(BORDER_STYLE, "solid");
}
else if (value instanceof EmptyBorder)
{
EmptyBorder border = (EmptyBorder)value;
map.put(TYPE, ComponentFactoryHelper.EMPTY_BORDER);
Map<String, Object> borderStyle = new HashMap<>();
map.put(BORDER_STYLE, borderStyle);
Insets in = border.getBorderInsets();
borderStyle.put(BORDER_WIDTH, in.top + "px " + in.right + "px " + in.bottom + "px " + in.left + "px ");
borderStyle.put(BORDER_COLOR, "rgba(0,0,0,0)");
}
else if (value instanceof TitledBorder)
{
TitledBorder border = (TitledBorder)value;
map.put(TYPE, ComponentFactoryHelper.TITLED_BORDER);
map.put(TITLE, border.getTitle());
map.put("font", border.getTitleFont());
map.put("color", border.getTitleColor());
int just = border.getTitleJustification();
String titleJust = "left";
switch (just)
{
case TitledBorder.LEFT :
titleJust = "left";
break;
case TitledBorder.RIGHT :
titleJust = "right";
break;
case TitledBorder.CENTER :
titleJust = "center";
break;
case TitledBorder.LEADING :
titleJust = "leading";
break;
case TitledBorder.TRAILING :
titleJust = "trailing";
break;
}
map.put(TITLE_JUSTIFICATION, titleJust);
String titlePosition = "Top";
switch (border.getTitlePosition())
{
case TitledBorder.ABOVE_TOP :
titlePosition = "Above top";
break;
case TitledBorder.BELOW_TOP :
titlePosition = "Below top";
break;
case TitledBorder.ABOVE_BOTTOM :
titlePosition = "Above bottom";
break;
case TitledBorder.BELOW_BOTTOM :
titlePosition = "Below bottom";
break;
case TitledBorder.BOTTOM :
titlePosition = "Bottom";
break;
}
map.put(TITLE_POSITION, titlePosition);
}
return map;
}
@Override
public Object parseConfig(JSONObject json)
{
return json != null && Boolean.valueOf(json.optBoolean("stringformat"));
}
@Override
public JSONWriter toTemplateJSONValue(JSONWriter writer, String key, Border formElementValue, PropertyDescription pd,
DataConversion browserConversionMarkers, FormElementContext formElementContext) throws JSONException
{
return toJSON(writer, key, formElementValue, pd, browserConversionMarkers, null);
}
@Override
public Border toFormElementValue(JSONObject designValue, PropertyDescription pd, FlattenedSolution flattenedSolution, INGFormElement formElement,
PropertyPath propertyPath)
{
return fromJSON(designValue, null, pd, null, null);
}
@Override
public Border toSabloComponentValue(Object rhinoValue, Border previousComponentValue, PropertyDescription pd, BaseWebObject componentOrService)
{
if (rhinoValue instanceof String)
{
return ComponentFactoryHelper.createBorder((String)rhinoValue);
}
return (Border)(rhinoValue instanceof Border ? rhinoValue : null);
}
@Override
public boolean isValueAvailableInRhino(Border webComponentValue, PropertyDescription pd, BaseWebObject componentOrService)
{
return true;
}
@Override
public Object toRhinoValue(Border webComponentValue, PropertyDescription pd, BaseWebObject componentOrService, Scriptable startScriptable)
{
return webComponentValue; // TODO any conversion needed here?
}
@Override
public Object fromRhinoToDesignValue(Object value, PropertyDescription pd, IApplication application, JSWebComponent webComponent)
{
if (value instanceof String)
{
Border border = ComponentFactoryHelper.createBorder((String)value);
JSONStringer writer = new JSONStringer();
try
{
writer.object();
toJSON(writer, pd.getName(), border, pd, new DataConversion(), null);
writer.endObject();
return new JSONObject(writer.toString()).get(pd.getName());
}
catch (JSONException e)
{
Debug.error(e);
}
}
return JSWebComponent.defaultRhinoToDesignValue(value, application);
}
@Override
public Object fromDesignToRhinoValue(Object value, PropertyDescription pd, IApplication application, JSWebComponent webComponent)
{
Border border = fromJSON(value, null, pd, null, null);
return ComponentFactoryHelper.createBorderString(border);
}
/*
* (non-Javadoc)
*
* @see org.sablo.specification.property.IDesignValueConverter#fromDesignValue(java.lang.Object, org.sablo.specification.PropertyDescription)
*/
@Override
public Border fromDesignValue(Object newValue, PropertyDescription propertyDescription)
{
try
{
return fromJSON((newValue instanceof String && ((String)newValue).startsWith("{")) ? new JSONObject((String)newValue) : newValue, null,
propertyDescription, null, null);
}
catch (Exception e)
{
Debug.error("can't parse '" + newValue + "' to the real type for property converter: " + propertyDescription.getType(), e);
return null;
}
}
/*
* (non-Javadoc)
*
* @see org.sablo.specification.property.IDesignValueConverter#toDesignValue(java.lang.Object, org.sablo.specification.PropertyDescription)
*/
@Override
public Object toDesignValue(Object value, PropertyDescription pd)
{
if (value instanceof Border)
{
JSONStringer writer = new JSONStringer();
toJSON(writer, null, (Border)value, pd, null, null);
return new JSONObject(writer.toString());
}
return value;
}
}