/* See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* Esri Inc. licenses this file to You 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.esri.gpt.catalog.gxe;
import com.esri.gpt.framework.jsf.MessageBroker;
import com.esri.gpt.framework.util.Val;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.faces.context.FacesContext;
/**
* Serializes a Geoportal XML editor definition to JSON format.
*/
public class GxeJsonSerializer {
/** class variables ========================================================= */
/** The Logger. */
private static Logger LOGGER = Logger.getLogger(GxeJsonSerializer.class.getName());
/** constructors ============================================================ */
/** Default constructor */
public GxeJsonSerializer() {
super();
}
/** methods ================================================================= */
/**
* Returns a JSON representation for a Geoportal XML editor definition.
* @param context the processing context
* @param definition the definition to be serialized
* @return the JSON string
* @throws IOException if an exception occurs
*/
public String asJson(GxeContext context, GxeDefinition definition) throws IOException {
PrintWriter pw = null;
try {
StringWriter sw = new StringWriter();
pw = new PrintWriter(sw);
this.toJson(context,definition.getRootElement(),pw,0,true);
pw.flush();
return sw.toString();
} finally {
try {if (pw != null) pw.close();} catch (Exception ef) {}
}
}
/**
* Evaluates a GXE XmlNode value.
* @param context the processing context
* @param xmlNode the GXE XmlNode that is actively being processed
* @param value the node value to evaluate
* @return the evaluated node value
*/
protected String evaluateNodeValue(GxeContext context, XmlNode xmlNode, String value) {
if (value == null) return null;
if (value.startsWith("$i18nBase")) {
boolean bIsI18NBaseAttribute = false;
XmlNodeInfo ni = xmlNode.ensureNodeInfo();
String ns = Val.chkStr(ni.getNamespaceURI());
String ln = Val.chkStr(ni.getLocalName());
if (ns.equals(GxeContext.URI_GXE) && ln.equals("i18nBase")) {
bIsI18NBaseAttribute = true;
}
if (!bIsI18NBaseAttribute) {
String i18nBase = "";
String i18nBaseSfx = "";
XmlElement parent = xmlNode.getParent();
while (parent != null) {
if (parent.getAttributes() != null) {
String tmp = Val.chkStr(parent.getAttributeValue(GxeContext.URI_GXE,"i18nBase"));
if (tmp.length() > 0) {
if (tmp.startsWith("$i18nBase")) {
i18nBaseSfx = Val.chkStr(tmp.substring(9))+i18nBaseSfx;
} else {
i18nBase = tmp+i18nBaseSfx;
break;
}
}
}
parent = parent.getParent();
}
if (i18nBase.length() > 0) {
String i18nKey = i18nBase+Val.chkStr(value.substring(9));
return this.lookupI18N(context,i18nKey,xmlNode,value);
} else {
return null;
}
}
//
} else if (value.startsWith("$i18n.")) {
String i18nKey = Val.chkStr(value.substring(6));
return this.lookupI18N(context,i18nKey,xmlNode,value);
} else if (value.startsWith("i18n:")) {
String i18nKey = Val.chkStr(value.substring(5));
return this.lookupI18N(context,i18nKey,xmlNode,value);
// a JSF based expression
} else if ((value.length() > 3) && (value.indexOf("#{") != -1)) {
String sExpr = value;
value = null;
try {
FacesContext fc = context.getFacesContext();
ExpressionFactory ef = fc.getApplication().getExpressionFactory();
ELContext elContext = fc.getELContext();
ValueExpression vExpr = ef.createValueExpression(elContext,sExpr,Object.class);
Object oValue = vExpr.getValue(elContext);
if (oValue != null) {
value = Val.chkStr(oValue.toString());
if (value.length() == 0) value = null;
}
} catch (Exception e) {
value = null;
String msg = "JSF expression failed to bind:\n"+sExpr+"\n"+e.toString();
LOGGER.log(Level.SEVERE,msg,e);
}
}
return value;
}
/**
* Gets the list of attributes to render.
* @param context the processing context
* @param element the GXE XmlElement that is actively being processed
* @return the list to render
*/
protected List<XmlAttribute> getAttributesToRender(GxeContext context, XmlElement element) {
List<XmlAttribute> list = new ArrayList<XmlAttribute>();
for (XmlAttribute child: element.getAttributes().values()) {
boolean bRender = true;
XmlNodeInfo ni = child.getNodeInfo();
String nsURI = ni.getNamespaceURI();
if ((nsURI != null) && nsURI.equals(GxeContext.URI_GXE)) {
String ln = ni.getLocalName();
if (ln.equals("extends")) {
bRender = false;
} else if (ln.equals("src")) {
bRender = false;
} else if (ln.equals("extensible")) {
bRender = false;
} else if (ln.equals("overridable")) {
bRender = false;
} else if (ln.equals("i18nBase")) {
bRender = false;
} else if (ln.equals("maxOccurs") || ln.equals("minOccurs") || ln.equals("use")) {
XmlNodeInfo ni2 = element.getNodeInfo();
String nsURI2 = ni2.getNamespaceURI();
if ((nsURI2 != null) && nsURI2.equals(GxeContext.URI_GXE)) {
String nv = Val.chkStr(ni.getNodeValue());
String ln2 = ni2.getLocalName();
if (ln2.equals("element")) {
if (ln.equals("maxOccurs") || ln.equals("minOccurs")) {
if (nv.equals("1")) bRender = false;
}
} else if (ln2.equals("attribute")) {
if (ln.equals("maxOccurs")) {
bRender = false;
} else if (ln.equals("minOccurs")) {
if (nv.equals("0")) bRender = false;
} else if (ln.equals("use")) {
if (nv.equals("optional")) bRender = false;
}
}
}
}
}
if (bRender) list.add(child);
}
return list;
}
/**
* Gets the list of children to render.
* @param context the processing context
* @param element the GXE XmlElement that is actively being processed
* @return the list to render
*/
protected List<XmlElement> getChildrenToRender(GxeContext context, XmlElement element) {
List<XmlElement> list = new ArrayList<XmlElement>();
for (XmlElement child: element.getChildren().values()) {
boolean bRender = true;
XmlNodeInfo ni = child.getNodeInfo();
String nsURI = ni.getNamespaceURI();
if ((nsURI != null) && nsURI.equals(GxeContext.URI_GXE)) {
String ln = ni.getLocalName();
if (ln.equals("annotation")) {
bRender = false;
} else if (ln.equals("documentation")) {
bRender = false;
} else {
if (bRender) {
String s = Val.chkStr(child.getAttributeValue(GxeContext.URI_GXE,"rendered"));
if (s.equals("$editor.isOriginalMode")) bRender = false;
else if (s.equals("$editor.isExpertMode")) bRender = false;
else if (s.equals("$editor.isAdvancedMode")) bRender = false;
else if (s.equals("$editor.isSimplifiedMode")) bRender = true;
/*
if (s.equals("$editor.isOriginalMode")) bRender = true;
else if (s.equals("$editor.isExpertMode")) bRender = false;
else if (s.equals("$editor.isAdvancedMode")) bRender = true;
else if (s.equals("$editor.isSimplifiedMode")) bRender = false;
*/
}
if (bRender) {
String s = Val.chkStr(child.getAttributeValue(GxeContext.URI_GXE,"jsClass"));
if (s.equals("gxe.control.SectionMenu")) bRender = false;
else if (s.equals("gxe.control.IndexedTabArray")) bRender = false;
}
}
}
if (bRender) list.add(child);
}
return list;
}
/**
* Looks up an I18N (internationalization and localization) message.
* @param context the processing context
* @param i18nKey the resource bundle key
* @param xmlNode the GXE XmlNode that is actively being processed
* @param originalValue the original node value
* @return the I18N message
*/
protected String lookupI18N(GxeContext context,
String i18nKey,
XmlNode xmlNode,
String originalValue) {
MessageBroker msgBroker = context.getMessageBroker();
String rVal = Val.chkStr(msgBroker.retrieveMessage(i18nKey));
if (rVal.length() > 0) {
if (rVal.startsWith("??")) {
return null;
} else {
//return rVal.toUpperCase();
}
}
return rVal;
}
/**
* Writes a JSON representation of the property.
* @param context the processing context
* @param element the GXE element that is actively being processed
* @param writer the writer
* @param depth the depth of the parent
* @param isLast <code>true</code if this is the last child element for a collection of children
* @throws IOException if an I/O exception occurs
*/
public void toJson(GxeContext context,
XmlElement element,
PrintWriter writer,
int depth,
boolean isLast) throws IOException {
String pfx = "";
String sep2 = "";
String sep4 = "";
boolean bIndent = false;
if (bIndent) {
sep2 = " ";
sep4 = " ";
for (int i=0;i<2*depth;i++) pfx += " ";
}
List<XmlAttribute> attributes = this.getAttributesToRender(context,element);
List<XmlElement> children = this.getChildrenToRender(context,element);
boolean compactGxdNS = true;
boolean hasChildren = (children.size() > 0);
String line;
writer.println(pfx+"{");
XmlNodeInfo nodeInfo = element.ensureNodeInfo();
String nsURI = nodeInfo.getNamespaceURI();
String name = nodeInfo.getLocalName();
String value = this.evaluateNodeValue(context,element,nodeInfo.getNodeValue());
if (compactGxdNS && (name.indexOf(":") == -1) && (nsURI != null) &&
(nsURI.equals(GxeContext.URI_GXE) || nsURI.equals(GxeContext.URI_GXEHTML))) {
if (nsURI.equals(GxeContext.URI_GXE)) name = "g:"+name;
else name = "h:"+name;
nsURI = null;
}
line = pfx+sep2;
if (nsURI != null) {
line +="\"namespace\":\""+Val.escapeStrForJson(nsURI)+"\",";
}
line +="\"name\":\""+Val.escapeStrForJson(name)+"\",";
if (value != null) {
line +=" \"value\":\""+Val.escapeStrForJson(value)+"\",";
}
writer.println(line);
line = pfx+sep2+"\"attributes\":[";
writer.println(line);
int nCount = 0;
int nLast = attributes.size();
//int nLast = element.getAttributes().size();
//for (XmlAttribute child: element.getAttributes().values()) {
for (XmlAttribute child: attributes) {
nCount++;
boolean bLast = (nCount == nLast);
nodeInfo = child.ensureNodeInfo();
nsURI = nodeInfo.getNamespaceURI();
name = nodeInfo.getLocalName();
value = this.evaluateNodeValue(context,child,nodeInfo.getNodeValue());
if (compactGxdNS && (name.indexOf(":") == -1) && (nsURI != null) &&
(nsURI.equals(GxeContext.URI_GXE) || nsURI.equals(GxeContext.URI_GXEHTML))) {
if (nsURI.equals(GxeContext.URI_GXE)) name = "g:"+name;
else name = "h:"+name;
nsURI = null;
}
line = pfx+sep4+"{";
if (nsURI != null) {
line +="\"namespace\":\""+Val.escapeStrForJson(nsURI)+"\",";
}
line +="\"name\":\""+Val.escapeStrForJson(name)+"\"";
if (value != null) {
line +=",";
line +="\"value\":\""+Val.escapeStrForJson(value)+"\"";
}
line +="}";
if (!bLast) line +=",";
writer.println(line);
}
if (hasChildren) {
writer.println(pfx+sep2+"],");
} else {
writer.println(pfx+sep2+"]");
}
if (hasChildren) {
line = pfx+sep2+"\"children\":[";
writer.println(line);
nCount = 0;
nLast = children.size();
for (XmlElement child: children) {
nCount++;
boolean bLast = (nCount == nLast);
this.toJson(context,child,writer,(depth+2),bLast);
}
writer.println(pfx+sep2+"]");
}
line = pfx+"}";
if (!isLast) line +=",";
writer.println(line);
}
}