/******************************************************************************* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF 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 org.apache.ofbiz.widget.model; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.regex.PatternSyntaxException; import org.apache.ofbiz.base.util.Debug; import org.apache.ofbiz.base.util.GeneralException; import org.apache.ofbiz.base.util.ScriptUtil; import org.apache.ofbiz.base.util.UtilFormatOut; import org.apache.ofbiz.base.util.UtilGenerics; import org.apache.ofbiz.base.util.UtilValidate; import org.apache.ofbiz.base.util.UtilXml; import org.apache.ofbiz.base.util.collections.FlexibleMapAccessor; import org.apache.ofbiz.base.util.string.FlexibleStringExpander; import org.apache.ofbiz.entity.finder.ByAndFinder; import org.apache.ofbiz.entity.finder.ByConditionFinder; import org.apache.ofbiz.entity.finder.EntityFinderUtil; import org.apache.ofbiz.entity.util.EntityListIterator; import org.apache.ofbiz.minilang.MiniLangException; import org.apache.ofbiz.minilang.SimpleMethod; import org.apache.ofbiz.minilang.method.MethodContext; import org.apache.ofbiz.service.DispatchContext; import org.apache.ofbiz.service.GenericServiceException; import org.apache.ofbiz.service.ModelService; import org.apache.ofbiz.widget.WidgetWorker; import org.apache.ofbiz.widget.model.ModelTree.ModelNode; import org.w3c.dom.Document; import org.w3c.dom.Element; /** * Abstract tree action. */ @SuppressWarnings("serial") public abstract class ModelTreeAction extends AbstractModelAction { /* * ----------------------------------------------------------------------- * * DEVELOPERS PLEASE READ * ----------------------------------------------------------------------- * * * This model is intended to be a read-only data structure that represents * an XML element. Outside of object construction, the class should not * have any behaviors. * * Instances of this class will be shared by multiple threads - therefore * it is immutable. DO NOT CHANGE THE OBJECT'S STATE AT RUN TIME! * */ public static final String module = ModelTreeAction.class.getName(); public static List<ModelAction> readNodeActions(ModelNode modelNode, Element actionsElement) { List<? extends Element> actionElementList = UtilXml.childElementList(actionsElement); List<ModelAction> actions = new ArrayList<ModelAction>(actionElementList.size()); for (Element actionElement : actionElementList) { if ("service".equals(actionElement.getNodeName())) { actions.add(new Service(modelNode, actionElement)); } else if ("script".equals(actionElement.getNodeName())) { actions.add(new Script(modelNode, actionElement)); } else { actions.add(AbstractModelAction.newInstance(modelNode, actionElement)); } } return actions; } public static List<ModelAction> readSubNodeActions(ModelNode.ModelSubNode modelSubNode, Element actionsElement) { List<? extends Element> actionElementList = UtilXml.childElementList(actionsElement); List<ModelAction> actions = new ArrayList<ModelAction>(actionElementList.size()); for (Element actionElement : actionElementList) { if ("service".equals(actionElement.getNodeName())) { actions.add(new Service(modelSubNode, actionElement)); } else if ("entity-and".equals(actionElement.getNodeName())) { actions.add(new EntityAnd(modelSubNode, actionElement)); } else if ("entity-condition".equals(actionElement.getNodeName())) { actions.add(new EntityCondition(modelSubNode, actionElement)); } else if ("script".equals(actionElement.getNodeName())) { actions.add(new Script(modelSubNode, actionElement)); } else { actions.add(AbstractModelAction.newInstance(modelSubNode, actionElement)); } } return actions; } private final ModelNode.ModelSubNode modelSubNode; private final ModelTree modelTree; protected ModelTreeAction(ModelNode modelNode, Element actionElement) { if (Debug.verboseOn()) Debug.logVerbose("Reading Tree action with name: " + actionElement.getNodeName(), module); this.modelTree = modelNode.getModelTree(); this.modelSubNode = null; } protected ModelTreeAction(ModelNode.ModelSubNode modelSubNode, Element actionElement) { if (Debug.verboseOn()) Debug.logVerbose("Reading Tree action with name: " + actionElement.getNodeName(), module); this.modelSubNode = modelSubNode; this.modelTree = modelSubNode.getNode().getModelTree(); } public ModelNode.ModelSubNode getModelSubNode() { return modelSubNode; } public ModelTree getModelTree() { return modelTree; } /** * Models the <entity-and> element. * * @see <code>widget-tree.xsd</code> */ public static class EntityAnd extends ModelTreeAction { private final ByAndFinder finder; private final String listName; public EntityAnd(ModelNode.ModelSubNode modelSubNode, Element entityAndElement) { super(modelSubNode, entityAndElement); boolean useCache = "true".equalsIgnoreCase(entityAndElement.getAttribute("use-cache")); Document ownerDoc = entityAndElement.getOwnerDocument(); if (!useCache) UtilXml.addChildElement(entityAndElement, "use-iterator", ownerDoc); String listName = UtilFormatOut.checkEmpty(entityAndElement.getAttribute("list"), entityAndElement.getAttribute("list-name")); if (UtilValidate.isEmpty(listName)) listName = "_LIST_ITERATOR_"; this.listName = listName; entityAndElement.setAttribute("list-name", this.listName); finder = new ByAndFinder(entityAndElement); } @Override public void accept(ModelActionVisitor visitor) throws Exception { visitor.visit(this); } public ByAndFinder getFinder() { return finder; } public String getListName() { return listName; } @Override public void runAction(Map<String, Object> context) { try { context.put(this.listName, null); finder.runFind(context, WidgetWorker.getDelegator(context)); Object obj = context.get(this.listName); if (obj != null && (obj instanceof EntityListIterator || obj instanceof ListIterator<?>)) { ListIterator<? extends Map<String, ? extends Object>> listIt = UtilGenerics.cast(obj); this.getModelSubNode().setListIterator(listIt, context); } else { if (obj instanceof List<?>) { List<? extends Map<String, ? extends Object>> list = UtilGenerics.checkList(obj); this.getModelSubNode().setListIterator(list.listIterator(), context); } } } catch (GeneralException e) { String errMsg = "Error doing entity query by condition: " + e.toString(); Debug.logError(e, errMsg, module); throw new IllegalArgumentException(errMsg); } } } /** * Models the <entity-condition> element. * * @see <code>widget-tree.xsd</code> */ public static class EntityCondition extends ModelTreeAction { private final ByConditionFinder finder; private final String listName; public EntityCondition(ModelNode.ModelSubNode modelSubNode, Element entityConditionElement) { super(modelSubNode, entityConditionElement); Document ownerDoc = entityConditionElement.getOwnerDocument(); boolean useCache = "true".equalsIgnoreCase(entityConditionElement.getAttribute("use-cache")); if (!useCache) UtilXml.addChildElement(entityConditionElement, "use-iterator", ownerDoc); String listName = UtilFormatOut.checkEmpty(entityConditionElement.getAttribute("list"), entityConditionElement.getAttribute("list-name")); if (UtilValidate.isEmpty(listName)) listName = "_LIST_ITERATOR_"; this.listName = listName; entityConditionElement.setAttribute("list-name", this.listName); finder = new ByConditionFinder(entityConditionElement); } @Override public void accept(ModelActionVisitor visitor) throws Exception { visitor.visit(this); } public ByConditionFinder getFinder() { return finder; } public String getListName() { return listName; } @Override public void runAction(Map<String, Object> context) { try { context.put(this.listName, null); finder.runFind(context, WidgetWorker.getDelegator(context)); Object obj = context.get(this.listName); if (obj != null && (obj instanceof EntityListIterator || obj instanceof ListIterator<?>)) { ListIterator<? extends Map<String, ? extends Object>> listIt = UtilGenerics.cast(obj); this.getModelSubNode().setListIterator(listIt, context); } else { if (obj instanceof List<?>) { List<? extends Map<String, ? extends Object>> list = UtilGenerics.cast(obj); this.getModelSubNode().setListIterator(list.listIterator(), context); } } } catch (GeneralException e) { String errMsg = "Error doing entity query by condition: " + e.toString(); Debug.logError(e, errMsg, module); throw new IllegalArgumentException(errMsg); } } } /** * Models the <script> element. * * @see <code>widget-tree.xsd</code> */ public static class Script extends ModelTreeAction { private final String location; private final String method; public Script(ModelNode modelNode, Element scriptElement) { super(modelNode, scriptElement); String scriptLocation = scriptElement.getAttribute("location"); this.location = WidgetWorker.getScriptLocation(scriptLocation); this.method = WidgetWorker.getScriptMethodName(scriptLocation); } public Script(ModelNode.ModelSubNode modelSubNode, Element scriptElement) { super(modelSubNode, scriptElement); String scriptLocation = scriptElement.getAttribute("location"); this.location = WidgetWorker.getScriptLocation(scriptLocation); this.method = WidgetWorker.getScriptMethodName(scriptLocation); } @Override public void accept(ModelActionVisitor visitor) throws Exception { visitor.visit(this); } public String getLocation() { return location; } public String getMethod() { return method; } @Override public void runAction(Map<String, Object> context) { context.put("_LIST_ITERATOR_", null); if (location.endsWith(".xml")) { Map<String, Object> localContext = new HashMap<String, Object>(); localContext.putAll(context); DispatchContext ctx = WidgetWorker.getDispatcher(context).getDispatchContext(); MethodContext methodContext = new MethodContext(ctx, localContext, null); try { SimpleMethod.runSimpleMethod(location, method, methodContext); context.putAll(methodContext.getResults()); } catch (MiniLangException e) { throw new RuntimeException("Error running simple method at location [" + location + "]", e); } } else { ScriptUtil.executeScript(this.location, this.method, context); } Object obj = context.get("_LIST_ITERATOR_"); if (this.getModelSubNode() != null) { if (obj != null && (obj instanceof EntityListIterator || obj instanceof ListIterator<?>)) { ListIterator<? extends Map<String, ? extends Object>> listIt = UtilGenerics.cast(obj); this.getModelSubNode().setListIterator(listIt, context); } else { if (obj instanceof List<?>) { List<? extends Map<String, ? extends Object>> list = UtilGenerics.checkList(obj); this.getModelSubNode().setListIterator(list.listIterator(), context); } } } } } /** * Models the <service> element. * * @see <code>widget-tree.xsd</code> */ public static class Service extends ModelTreeAction { private final FlexibleStringExpander autoFieldMapExdr; private final Map<FlexibleMapAccessor<Object>, Object> fieldMap; private final FlexibleStringExpander resultMapListNameExdr; private final FlexibleMapAccessor<Map<String, Object>> resultMapNameAcsr; private final FlexibleStringExpander resultMapValueNameExdr; private final FlexibleStringExpander serviceNameExdr; private final FlexibleStringExpander valueNameExdr; public Service(ModelNode modelNode, Element serviceElement) { super(modelNode, serviceElement); this.serviceNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("service-name")); this.resultMapNameAcsr = FlexibleMapAccessor.getInstance(serviceElement.getAttribute("result-map")); this.autoFieldMapExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("auto-field-map")); this.resultMapListNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("result-map-list")); this.resultMapValueNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("result-map-value")); this.valueNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("value")); this.fieldMap = EntityFinderUtil.makeFieldMap(serviceElement); } public Service(ModelNode.ModelSubNode modelSubNode, Element serviceElement) { super(modelSubNode, serviceElement); this.serviceNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("service-name")); this.resultMapNameAcsr = FlexibleMapAccessor.getInstance(serviceElement.getAttribute("result-map")); this.autoFieldMapExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("auto-field-map")); this.resultMapListNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("result-map-list")); this.resultMapValueNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("result-map-value")); this.valueNameExdr = FlexibleStringExpander.getInstance(serviceElement.getAttribute("value")); this.fieldMap = EntityFinderUtil.makeFieldMap(serviceElement); } @Override public void accept(ModelActionVisitor visitor) throws Exception { visitor.visit(this); } public FlexibleStringExpander getAutoFieldMapExdr() { return autoFieldMapExdr; } public Map<FlexibleMapAccessor<Object>, Object> getFieldMap() { return fieldMap; } public FlexibleStringExpander getResultMapListNameExdr() { return resultMapListNameExdr; } public FlexibleMapAccessor<Map<String, Object>> getResultMapNameAcsr() { return resultMapNameAcsr; } public FlexibleStringExpander getResultMapValueNameExdr() { return resultMapValueNameExdr; } public FlexibleStringExpander getServiceNameExdr() { return serviceNameExdr; } public FlexibleStringExpander getValueNameExdr() { return valueNameExdr; } @Override public void runAction(Map<String, Object> context) { String serviceNameExpanded = this.serviceNameExdr.expandString(context); if (UtilValidate.isEmpty(serviceNameExpanded)) { throw new IllegalArgumentException("Service name was empty, expanded from: " + this.serviceNameExdr.getOriginal()); } String autoFieldMapString = this.autoFieldMapExdr.expandString(context); boolean autoFieldMapBool = !"false".equals(autoFieldMapString); try { Map<String, Object> serviceContext = null; if (autoFieldMapBool) { serviceContext = WidgetWorker.getDispatcher(context).getDispatchContext() .makeValidContext(serviceNameExpanded, ModelService.IN_PARAM, context); } else { serviceContext = new HashMap<String, Object>(); } if (this.fieldMap != null) { EntityFinderUtil.expandFieldMapToContext(this.fieldMap, context, serviceContext); } Map<String, Object> result = WidgetWorker.getDispatcher(context).runSync(serviceNameExpanded, serviceContext); if (!this.resultMapNameAcsr.isEmpty()) { this.resultMapNameAcsr.put(context, result); String queryString = (String) result.get("queryString"); context.put("queryString", queryString); context.put("queryStringMap", result.get("queryStringMap")); if (UtilValidate.isNotEmpty(queryString)) { try { String queryStringEncoded = queryString.replaceAll("&", "%26"); context.put("queryStringEncoded", queryStringEncoded); } catch (PatternSyntaxException e) { } } } else { context.putAll(result); } String resultMapListName = resultMapListNameExdr.expandString(context); //String resultMapListIteratorName = resultMapListIteratorNameExdr.expandString(context); String resultMapValueName = resultMapValueNameExdr.expandString(context); String valueName = valueNameExdr.expandString(context); if (this.getModelSubNode() != null) { //ListIterator iter = null; if (UtilValidate.isNotEmpty(resultMapListName)) { List<? extends Map<String, ? extends Object>> lst = UtilGenerics.checkList(result.get(resultMapListName)); if (lst != null) { if (lst instanceof ListIterator<?>) { ListIterator<? extends Map<String, ? extends Object>> listIt = UtilGenerics.cast(lst); this.getModelSubNode().setListIterator(listIt, context); } else { this.getModelSubNode().setListIterator(lst.listIterator(), context); } } } } else { if (UtilValidate.isNotEmpty(resultMapValueName)) { if (UtilValidate.isNotEmpty(valueName)) { context.put(valueName, result.get(resultMapValueName)); } else { Map<String, Object> resultMap = UtilGenerics.checkMap(result.get(resultMapValueName)); context.putAll(resultMap); } } } } catch (GenericServiceException e) { String errMsg = "Error calling service with name " + serviceNameExpanded + ": " + e.toString(); Debug.logError(e, errMsg, module); throw new IllegalArgumentException(errMsg); } } } }