All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.adobe.aemds.guide.xfa.XFAJSONTransformer Maven / Gradle / Ivy

/*************************************************************************
 *
 * ADOBE CONFIDENTIAL
 * __________________
 *
 *  Copyright 2014 Adobe Systems Incorporated
 *  All Rights Reserved.
 *
 * NOTICE:  All information contained herein is, and remains
 * the property of Adobe Systems Incorporated and its suppliers,
 * if any.  The intellectual and technical concepts contained
 * herein are proprietary to Adobe Systems Incorporated and its
 * suppliers and may be covered by U.S. and Foreign Patents,
 * patents in process, and are protected by trade secret or copyright law.
 * Dissemination of this information or reproduction of this material
 * is strictly forbidden unless prior written permission is obtained
 * from Adobe Systems Incorporated.
 **************************************************************************/

package com.adobe.aemds.guide.xfa;

import com.adobe.aemds.guide.service.GuideException;
import com.adobe.aemds.guide.utils.GuideConstants;
import com.day.cq.commons.jcr.JcrConstants;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.commons.json.JSONArray;
import org.apache.sling.commons.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.StringWriter;
import java.util.*;

/**
 * @pad.exclude Exclude from Published API.
 */
public class XFAJSONTransformer {

    private Logger logger = LoggerFactory.getLogger(XFAJSONTransformer.class);

    private static List panelCandidates =
            Arrays.asList(new String[]{"area", "subform", "subformSet"});

    private static List leafCandidates =
            Arrays.asList(new String[]{"draw", "exclGroup", "field"});

    private static List unsupportedUiTypes = Arrays.asList(new String[]{
            "barcode", "exObject", "signature"
    });

    private static List unsupportedValueTypes = Arrays.asList(new String[]{
            "arc", "line", "rectangle"
    });

    private JSONObject xfaJson;
    private XFAJSONWriter jsonWriter;
    private StringWriter stringWriter;
    private Hashtable xfaObjects;

    private XFAJSONTransformerUtil transformUtil;
    //Used to manage jcr path element for same named field
    private Map currentContainerChildren = new HashMap();
    private ResourceResolver resourceResolver = null;

    public XFAJSONTransformer(JSONObject xfaJson) {
        this(xfaJson, null, "");
    }
    public XFAJSONTransformer(JSONObject xfaJson,ResourceResolver resourceResolver, String xfaPath) {
        this.xfaJson = xfaJson;
        this.stringWriter = new StringWriter();
        this.jsonWriter = new XFAJSONWriter(this.stringWriter);
        transformUtil = new XFAJSONTransformerUtil(resourceResolver, xfaPath);
    }

    public XFAJSONTransformer(JSONObject xfaJson, boolean saveObjects, ResourceResolver resourceResolver, String xfaPath) {
        this(xfaJson, resourceResolver, xfaPath);
        xfaObjects = new Hashtable();
    }

    public boolean acceptObject(final JSONObject jsonObject) throws Exception {
        String childType = jsonObject.getString("_class");
        if (!(panelCandidates.contains(childType) || leafCandidates.contains(childType))) {
            return false;
        }
        if (leafCandidates.contains(childType) && !this.acceptLeaf(jsonObject)) {
            return false;
        }
        return true;
    }

    public String transform() {
        try {
            JSONObject rootSubformJson = (JSONObject) transformUtil.getOrElse(this.xfaJson,
                    "form.subform", null, false);
            String rootSubformName = transformUtil.getOrGenerateName(rootSubformJson);
            jsonWriter.startObject(null)
                      .startObject(rootSubformName);
            transformContainer(rootSubformJson);
            jsonWriter.completeObject(rootSubformName)
                      .completeObject(null);
            return this.stringWriter.toString();
        } catch (Exception e) {
            logger.error("Error in transforming xfa json:" + e.getMessage(), e);
            throw new GuideException(e);
        }
    }

    private void transformChildren(JSONObject containerJson, boolean isChildOfTable) throws Exception{
        if (containerJson.has("children") && containerJson.get("children") instanceof JSONArray) {
            Map oldChildMap = this.currentContainerChildren;
            this.currentContainerChildren = new HashMap();
            try {
                JSONObject lastInstanceManager = null;
                jsonWriter.startObject("items");
                JSONArray containerChildren = containerJson.getJSONArray("children");
                for (int i = 0; i < containerChildren.length(); i++) {
                    JSONObject childJson = containerChildren.getJSONObject(i);
                    String childType = childJson.getString("_class");
                    if(!"subform".equals(childType)){
                        // InstanceManager should be reset if current child is not subform
                        lastInstanceManager = null;
                    }
                    if("instanceManager".equals(childType)){
                        //InstanceManager should be transformed with it's corresponding subform.
                        lastInstanceManager = childJson;
                        continue;
                    }

                    if(acceptObject(childJson)) {
                        String childName = transformUtil.getOrGenerateName(childJson);
                        int childIndex = -1;
                        if (currentContainerChildren.containsKey(childName)) {
                            childIndex = currentContainerChildren.get(childName);
                        }
                        childIndex++;
                        currentContainerChildren.put(childName, childIndex);
                        childName = childName + ((childIndex == 0) ? "" : childIndex);
                        jsonWriter.startObject(childName);
                        if (panelCandidates.contains(childType)) {
                            // even header of XDP has "layout" property as row
                            // todo: since header is optional in XDP, how to handle that ?
                            if(isChildOfTable && childJson.has("layout") && "row".equals((String)childJson.get("layout"))){
                                transformTableChild(childJson);
                            } else {
                                transformContainer(childJson);
                            }
                            if(lastInstanceManager!=null){
                                /*
                                * Currently we do not support explicit initial count in panel. Though Guide runtime would automatically handle for XFA Guides.
                                */
                                if(lastInstanceManager.has("min")){
                                    jsonWriter.key("minOccur").value(lastInstanceManager.getInt("min"));
                                }
                                if(lastInstanceManager.has("max")){
                                    jsonWriter.key("maxOccur").value(lastInstanceManager.getInt("max"));
                                }
                            }
                        } else if ("exclGroup".equals(childType)) {
                            transformExclGroup(childJson, isChildOfTable);
                        } else if ("draw".equals(childType)) {
                            //Need to handle rich text encoding before enabling below.
                            transformDraw(childJson, isChildOfTable);
                        } else {
                            transformField(childJson, isChildOfTable);
                        }
                        jsonWriter.completeObject(childName);
                    }
                }
                jsonWriter.completeObject("items");
            } finally {
                this.currentContainerChildren = oldChildMap;
            }
        }
    }

    private void transformContainer(final JSONObject containerJson, boolean isJsonTable) throws Exception {
        if(isJsonTable){
            // check if it is table, then transform it into table
            transformTable(containerJson);
        } else {
            transformPanel(containerJson);
        }
        jsonWriter.startObject("layout");
        Hashtable hash = transformUtil.getLayoutProperties(null, isJsonTable);
        jsonWriter.writeProperties(hash);
        jsonWriter.completeObject("layout");
        transformChildren(containerJson, isJsonTable);
    }

    /**
     * Checks if the given XDP table is supported by adaptive forms
     *
     * In adaptive forms, we don't support following:
     * a) Table with multiple headers
     * b) Table with no headers
     * c) Table within table
     *
     * @param containerJson
     * @return
     * @throws Exception
     */
    public boolean checkIfSupportedTable(final JSONObject containerJson) throws Exception {
        return checkIfSupportedTable(containerJson, 0);
    }

    private boolean checkIfSupportedTable(final JSONObject containerJson, int tableHeaderCount) throws Exception {
        if (containerJson.has("children") && containerJson.get("children") instanceof JSONArray) {
            JSONArray containerChildren = containerJson.getJSONArray("children");
            for (int i = 0; i < containerChildren.length(); i++) {
                JSONObject childJson = containerChildren.getJSONObject(i);
                String childType = childJson.getString("_class");
                if(acceptObject(childJson)) {
                    if (panelCandidates.contains(childType)) {
                        // even header of XDP has "layout" property as row
                        if(childJson.has("layout") && "table".equals((String)childJson.get("layout"))){
                            return false;
                        }  else {
                            if(tableHeaderCount == 0){
                                // this must be a table header always
                                if(checkIfContainerIsTableHeader(childJson)){
                                    boolean isValidTable = checkIfSupportedTable(childJson, tableHeaderCount++);
                                    if (!isValidTable) {
                                        return false;
                                    }
                                } else {
                                    // if first child is not header, we don't support tables without headers
                                    return false;
                                }
                            }  else {
                                // If the header count in table present in XDP is greater than 1, exit
                                if(checkIfContainerIsTableHeader(childJson)){
                                    // we don't support multiple headers
                                    return false;
                                }
                                boolean isValidTable = checkIfSupportedTable(childJson, tableHeaderCount);
                                if(!isValidTable){
                                    // if there is table inside table, we don't support
                                    return false;
                                }
                            }
                        }
                    }
                }
            }
        }
        return true;
    }

    private void transformContainer(final JSONObject containerJson) throws Exception {
         // check if the container is a table
         boolean bIsJsonTable = false;
         if(containerJson.has("layout")){
            if(containerJson.get("layout") instanceof String) {
                String layout = (String)containerJson.get("layout");
                if(layout != null && layout.length() > 0 && "table".equals(layout)){
                    bIsJsonTable = checkIfSupportedTable(containerJson, 0);
                }
            }
         }
        transformContainer(containerJson, bIsJsonTable);
    }

    /**
     * Checks if the JSON is a table header
     * This uses assist class to find the same
     * @param containerJson
     * @return
     * @throws Exception
     */
    private boolean checkIfContainerIsTableHeader(final JSONObject containerJson) throws Exception{
        boolean isHeader = false;
        JSONArray containerChildren = containerJson.getJSONArray("children");
        for (int i = 0; i < containerChildren.length(); i++) {
            JSONObject childJson = containerChildren.getJSONObject(i);
            String childType = childJson.getString("_class");
            if("assist".equals(childType)) {
                isHeader = "TH".equals(childJson.getString("role"));
                break;
            }
        }
        return isHeader;
    }

    /**
     * Transforms the table child element from the container json into adaptive forms table
     * @param containerJson
     * @throws Exception
     */
    private void transformTableChild(final JSONObject containerJson) throws Exception {
        // todo: check if table child also is a table
        // in that case: fallback to panel hierarchy
        Hashtable hash = transformUtil.getCommonProperties(null, containerJson);
        boolean bIsHeader = checkIfContainerIsTableHeader(containerJson);
        // check if the current container is a header using
        if(bIsHeader){
            hash.put(GuideConstants.SLING_RESOURCE_TYPE, GuideConstants.RT_TABLE_HEADER);
        } else {
            hash.put(GuideConstants.SLING_RESOURCE_TYPE, GuideConstants.RT_TABLE_ROW);
        }
        hash.put(GuideConstants.ELEMENT_PROPERTY_NODECLASS, GuideConstants.GUIDE_TABLE_ROW);
        if(hash.containsKey("bindRef")) {
            transformObjectFromHash(hash);
        } else {
            logger.warn("XFA Element for: "+hash.get(JcrConstants.JCR_TITLE)+" does not contain bindRef.");
        }
        hash.clear();
        jsonWriter.startObject("layout");
        String layoutResourceType = null;
        // Based on the index set the layout
        if(bIsHeader){
            layoutResourceType = GuideConstants.LAYOUT_TABLE_HEADER_LAYOUT;
        } else {
            layoutResourceType = GuideConstants.LAYOUT_TABLE_ROW_LAYOUT;
        }
        hash.put(JcrConstants.JCR_PRIMARYTYPE, GuideConstants.NT_UNSTRUCTURED);
        hash.put(GuideConstants.SLING_RESOURCE_TYPE, layoutResourceType);
        jsonWriter.writeProperties(hash);
        jsonWriter.completeObject("layout");
        transformChildren(containerJson, true);
    }


    private String getJsonStringFromHash(Hashtable hash) {
        StringWriter writer = new StringWriter();
        XFAJSONWriter jsonWriter = new XFAJSONWriter(writer);
        jsonWriter.startObject(null);
        jsonWriter.writeProperties(hash);
        jsonWriter.completeObject(null);
        return writer.toString();
    }

    /**
     * Transforms XDP Table into adaptive forms Table
     *
     * @param tableJson
     * @throws Exception
     */
    public void transformTable(JSONObject tableJson) throws Exception {
        Hashtable hash = transformUtil.getCommonProperties(null, tableJson);
        hash = transformUtil.getTableProperties(hash);
        if(hash.containsKey("bindRef")) {
            transformObjectFromHash(hash);
        } else {
            logger.warn("XFA Element for: "+hash.get(JcrConstants.JCR_TITLE)+" does not contain bindRef.");
        }
    }

    private void transformPanel(JSONObject panelJson) throws Exception {
        Hashtable hash = transformUtil.getCommonProperties(null, panelJson);
        hash = transformUtil.getPanelProperties(hash);
        if(hash.containsKey("bindRef")) {
            transformObjectFromHash(hash);
        } else {
            logger.warn("XFA Element for: "+hash.get(JcrConstants.JCR_TITLE)+" does not contain bindRef.");
        }
    }

    private void transformObjectFromHash(Hashtable hash) {
        String jsonString = getJsonStringFromHash(hash);
        if(xfaObjects != null) {
            xfaObjects.put((String)hash.get("bindRef"), jsonString);
        }
        hash.put("xfajson", jsonString);
        jsonWriter.writeProperties(hash);
    }

    private void transformField(JSONObject fieldJson, boolean isChildOfTable) throws Exception {
        Hashtable input = transformUtil.getFieldProperties(null, fieldJson);
        // check if child of table and has colSpan then add it to hash
        if(isChildOfTable && fieldJson.has("colSpan")){
            input.put("colspan", fieldJson.get("colSpan"));
        }
        transformObjectFromHash(input);
    }

    private void transformDraw(JSONObject drawJson, boolean isChildOfTable) throws Exception {
        Hashtable input = transformUtil.getDrawProperties(null, drawJson);
        if(isChildOfTable && drawJson.has("colSpan")){
            input.put("colspan", drawJson.get("colSpan"));
        }
        transformObjectFromHash(input);
    }

    private void transformExclGroup(JSONObject exclGroupJson, boolean isChildOfTable) throws Exception {
        Hashtable input = transformUtil.getExclGroupProperties(null, exclGroupJson);
        if(isChildOfTable && exclGroupJson.has("colSpan")){
            input.put("colspan", exclGroupJson.get("colSpan"));
        }
        transformObjectFromHash(input);
    }

    public final Hashtable getXFAObjects() {
        return xfaObjects;
    }

    public boolean acceptLeaf(JSONObject leafJson) throws Exception {
        String childType = leafJson.getString("_class");
        if ("draw".equals(childType) || "field".equals(childType)) {
            String fieldUiType = (String) transformUtil.getOrElse(leafJson, "ui.oneOfChild._class", null, true);
            if (unsupportedUiTypes.contains(fieldUiType)) {
                return false; //Not supporting above fields for now
            } else if ("defaultUi".equals(fieldUiType)) {
                String valueType = (String) transformUtil.getOrElse(leafJson, "value.oneOfChild._class",
                        null, true);
                if (unsupportedValueTypes.contains(valueType)) {
                    return false; //Unsupported draw
                }
            }
            return true;
        } else if ("exclGroup".equals(childType)) {
            return true;
        }
        return false;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy