You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ofbiz.apache.org by ad...@apache.org on 2015/01/17 17:47:24 UTC

svn commit: r1652638 [2/6] - in /ofbiz/trunk/framework/widget: dtd/ src/org/ofbiz/widget/ src/org/ofbiz/widget/artifact/ src/org/ofbiz/widget/fo/ src/org/ofbiz/widget/form/ src/org/ofbiz/widget/html/ src/org/ofbiz/widget/menu/ src/org/ofbiz/widget/scre...

Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelAction.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelAction.java?rev=1652638&view=auto
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelAction.java (added)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelAction.java Sat Jan 17 16:47:23 2015
@@ -0,0 +1,951 @@
+/*******************************************************************************
+ * 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.ofbiz.widget;
+
+import java.io.Serializable;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+import java.util.regex.PatternSyntaxException;
+
+import javax.servlet.ServletContext;
+import javax.servlet.http.HttpSession;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.GeneralException;
+import org.ofbiz.base.util.ObjectType;
+import org.ofbiz.base.util.ScriptUtil;
+import org.ofbiz.base.util.StringUtil;
+import org.ofbiz.base.util.UtilGenerics;
+import org.ofbiz.base.util.UtilProperties;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.UtilXml;
+import org.ofbiz.base.util.collections.FlexibleMapAccessor;
+import org.ofbiz.base.util.collections.ResourceBundleMapWrapper;
+import org.ofbiz.base.util.string.FlexibleStringExpander;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.finder.ByAndFinder;
+import org.ofbiz.entity.finder.ByConditionFinder;
+import org.ofbiz.entity.finder.EntityFinderUtil;
+import org.ofbiz.entity.finder.PrimaryKeyFinder;
+import org.ofbiz.entity.util.EntityUtilProperties;
+import org.ofbiz.minilang.MiniLangException;
+import org.ofbiz.minilang.SimpleMethod;
+import org.ofbiz.minilang.method.MethodContext;
+import org.ofbiz.service.DispatchContext;
+import org.ofbiz.service.GenericServiceException;
+import org.ofbiz.service.ModelService;
+import org.ofbiz.widget.xml.XmlWidgetActionVisitor;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract base class for the action models.
+ */
+@SuppressWarnings("serial")
+public abstract class AbstractModelAction implements Serializable, ModelAction {
+
+    /*
+     * ----------------------------------------------------------------------- *
+     *                     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 = AbstractModelAction.class.getName();
+
+    /**
+     * Returns a new <code>ModelAction</code> instance, built from <code>actionElement</code>.
+     * 
+     * @param modelWidget The <code>ModelWidget</code> that contains the &lt;actions&gt; element
+     * @param actionElement
+     * @return A new <code>ModelAction</code> instance
+     */
+    public static ModelAction newInstance(ModelWidget modelWidget, Element actionElement) {
+        if ("set".equals(actionElement.getNodeName())) {
+            return new SetField(modelWidget, actionElement);
+        } else if ("property-map".equals(actionElement.getNodeName())) {
+            return new PropertyMap(modelWidget, actionElement);
+        } else if ("property-to-field".equals(actionElement.getNodeName())) {
+            return new PropertyToField(modelWidget, actionElement);
+        } else if ("script".equals(actionElement.getNodeName())) {
+            return new Script(modelWidget, actionElement);
+        } else if ("service".equals(actionElement.getNodeName())) {
+            return new Service(modelWidget, actionElement);
+        } else if ("entity-one".equals(actionElement.getNodeName())) {
+            return new EntityOne(modelWidget, actionElement);
+        } else if ("entity-and".equals(actionElement.getNodeName())) {
+            return new EntityAnd(modelWidget, actionElement);
+        } else if ("entity-condition".equals(actionElement.getNodeName())) {
+            return new EntityCondition(modelWidget, actionElement);
+        } else if ("get-related-one".equals(actionElement.getNodeName())) {
+            return new GetRelatedOne(modelWidget, actionElement);
+        } else if ("get-related".equals(actionElement.getNodeName())) {
+            return new GetRelated(modelWidget, actionElement);
+        } else {
+            throw new IllegalArgumentException("Action element not supported with name: " + actionElement.getNodeName());
+        }
+    }
+
+    public static List<ModelAction> readSubActions(ModelWidget modelWidget, Element parentElement) {
+        List<? extends Element> actionElementList = UtilXml.childElementList(parentElement);
+        List<ModelAction> actions = new ArrayList<ModelAction>(actionElementList.size());
+        for (Element actionElement : actionElementList) {
+            actions.add(newInstance(modelWidget, actionElement));
+        }
+        return Collections.unmodifiableList(actions);
+    }
+
+    /**
+     * Executes the actions contained in <code>actions</code>.
+     * 
+     * @param actions
+     * @param context
+     */
+    public static void runSubActions(List<ModelAction> actions, Map<String, Object> context) {
+        if (actions == null)
+            return;
+        for (ModelAction action : actions) {
+            if (Debug.verboseOn())
+                Debug.logVerbose("Running action " + action.getClass().getName(), module);
+            try {
+                action.runAction(context);
+            } catch (GeneralException e) {
+                throw new RuntimeException(e);
+            }
+        }
+    }
+
+    private final ModelWidget modelWidget;
+
+    protected AbstractModelAction() {
+        // FIXME: This should not be null.
+        this.modelWidget = null;
+    }
+
+    protected AbstractModelAction(ModelWidget modelWidget, Element actionElement) {
+        this.modelWidget = modelWidget;
+        if (Debug.verboseOn())
+            Debug.logVerbose("Reading widget action with name: " + actionElement.getNodeName(), module);
+    }
+
+    /**
+     * Returns the <code>ModelWidget</code> that contains the &lt;actions&gt; element.
+     * 
+     * @return The <code>ModelWidget</code> that contains the &lt;actions&gt; element
+     */
+    public ModelWidget getModelWidget() {
+        return modelWidget;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        ModelActionVisitor visitor = new XmlWidgetActionVisitor(sb);
+        try {
+            accept(visitor);
+        } catch (Exception e) {
+            Debug.logWarning(e, "Exception thrown in XmlWidgetActionVisitor: ", module);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Models the &lt;entity-and&gt; element.
+     * 
+     * @see <code>widget-screen.xsd</code>
+     */
+    public static class EntityAnd extends AbstractModelAction {
+        private final ByAndFinder finder;
+
+        public EntityAnd(ModelWidget modelWidget, Element entityAndElement) {
+            super(modelWidget, entityAndElement);
+            finder = new ByAndFinder(entityAndElement);
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public ByAndFinder getFinder() {
+            return this.finder;
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) {
+            try {
+                finder.runFind(context, WidgetWorker.getDelegator(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 &lt;entity-condition&gt; element.
+     * 
+     * @see <code>widget-screen.xsd</code>
+     */
+    public static class EntityCondition extends AbstractModelAction {
+        private final ByConditionFinder finder;
+
+        public EntityCondition(ModelWidget modelWidget, Element entityConditionElement) {
+            super(modelWidget, entityConditionElement);
+            finder = new ByConditionFinder(entityConditionElement);
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public ByConditionFinder getFinder() {
+            return this.finder;
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) {
+            try {
+                finder.runFind(context, WidgetWorker.getDelegator(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 &lt;entity-one&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class EntityOne extends AbstractModelAction {
+        private final PrimaryKeyFinder finder;
+
+        public EntityOne(ModelWidget modelWidget, Element entityOneElement) {
+            super(modelWidget, entityOneElement);
+            finder = new PrimaryKeyFinder(entityOneElement);
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public PrimaryKeyFinder getFinder() {
+            return this.finder;
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) {
+            try {
+                finder.runFind(context, WidgetWorker.getDelegator(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 &lt;get-related&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class GetRelated extends AbstractModelAction {
+        private final FlexibleMapAccessor<List<GenericValue>> listNameAcsr;
+        private final FlexibleMapAccessor<Map<String, Object>> mapAcsr;
+        private final FlexibleMapAccessor<List<String>> orderByListAcsr;
+        private final String relationName;
+        private final boolean useCache;
+        private final FlexibleMapAccessor<Object> valueNameAcsr;
+
+        public GetRelated(ModelWidget modelWidget, Element getRelatedElement) {
+            super(modelWidget, getRelatedElement);
+            this.valueNameAcsr = FlexibleMapAccessor.getInstance(getRelatedElement.getAttribute("value-field"));
+            this.listNameAcsr = FlexibleMapAccessor.getInstance(getRelatedElement.getAttribute("list"));
+            this.relationName = getRelatedElement.getAttribute("relation-name");
+            this.mapAcsr = FlexibleMapAccessor.getInstance(getRelatedElement.getAttribute("map"));
+            this.orderByListAcsr = FlexibleMapAccessor.getInstance(getRelatedElement.getAttribute("order-by-list"));
+            this.useCache = "true".equals(getRelatedElement.getAttribute("use-cache"));
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public String getRelationName() {
+            return this.relationName;
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) {
+            Object valueObject = valueNameAcsr.get(context);
+            if (valueObject == null) {
+                Debug.logVerbose("Value not found with name: " + valueNameAcsr + ", not getting related...", module);
+                return;
+            }
+            if (!(valueObject instanceof GenericValue)) {
+                String errMsg = "Env variable for value-name " + valueNameAcsr.toString()
+                        + " is not a GenericValue object; for the relation-name: " + relationName + "]";
+                Debug.logError(errMsg, module);
+                throw new IllegalArgumentException(errMsg);
+            }
+            GenericValue value = (GenericValue) valueObject;
+            List<String> orderByNames = null;
+            if (!orderByListAcsr.isEmpty()) {
+                orderByNames = orderByListAcsr.get(context);
+            }
+            Map<String, Object> constraintMap = null;
+            if (!mapAcsr.isEmpty()) {
+                constraintMap = mapAcsr.get(context);
+            }
+            try {
+                listNameAcsr.put(context, value.getRelated(relationName, constraintMap, orderByNames, useCache));
+            } catch (GenericEntityException e) {
+                String errMsg = "Problem getting related from entity with name " + value.getEntityName()
+                        + " for the relation-name: " + relationName + ": " + e.getMessage();
+                Debug.logError(e, errMsg, module);
+                throw new IllegalArgumentException(errMsg);
+            }
+        }
+
+        public FlexibleMapAccessor<List<GenericValue>> getListNameAcsr() {
+            return listNameAcsr;
+        }
+
+        public FlexibleMapAccessor<Map<String, Object>> getMapAcsr() {
+            return mapAcsr;
+        }
+
+        public FlexibleMapAccessor<List<String>> getOrderByListAcsr() {
+            return orderByListAcsr;
+        }
+
+        public boolean getUseCache() {
+            return useCache;
+        }
+
+        public FlexibleMapAccessor<Object> getValueNameAcsr() {
+            return valueNameAcsr;
+        }
+    }
+
+    /**
+     * Models the &lt;get-related-one&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class GetRelatedOne extends AbstractModelAction {
+        private final String relationName;
+        private final FlexibleMapAccessor<Object> toValueNameAcsr;
+        private final boolean useCache;
+        private final FlexibleMapAccessor<Object> valueNameAcsr;
+
+        public GetRelatedOne(ModelWidget modelWidget, Element getRelatedOneElement) {
+            super(modelWidget, getRelatedOneElement);
+            this.valueNameAcsr = FlexibleMapAccessor.getInstance(getRelatedOneElement.getAttribute("value-field"));
+            this.toValueNameAcsr = FlexibleMapAccessor.getInstance(getRelatedOneElement.getAttribute("to-value-field"));
+            this.relationName = getRelatedOneElement.getAttribute("relation-name");
+            this.useCache = "true".equals(getRelatedOneElement.getAttribute("use-cache"));
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public String getRelationName() {
+            return this.relationName;
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) {
+            Object valueObject = valueNameAcsr.get(context);
+            if (valueObject == null) {
+                Debug.logVerbose("Value not found with name: " + valueNameAcsr + ", not getting related...", module);
+                return;
+            }
+            if (!(valueObject instanceof GenericValue)) {
+                String errMsg = "Env variable for value-name " + valueNameAcsr.toString()
+                        + " is not a GenericValue object; for the relation-name: " + relationName + "]";
+                Debug.logError(errMsg, module);
+                throw new IllegalArgumentException(errMsg);
+            }
+            GenericValue value = (GenericValue) valueObject;
+            try {
+                toValueNameAcsr.put(context, value.getRelatedOne(relationName, useCache));
+            } catch (GenericEntityException e) {
+                String errMsg = "Problem getting related one from entity with name " + value.getEntityName()
+                        + " for the relation-name: " + relationName + ": " + e.getMessage();
+                Debug.logError(e, errMsg, module);
+                throw new IllegalArgumentException(errMsg);
+            }
+        }
+
+        public FlexibleMapAccessor<Object> getToValueNameAcsr() {
+            return toValueNameAcsr;
+        }
+
+        public boolean getUseCache() {
+            return useCache;
+        }
+
+        public FlexibleMapAccessor<Object> getValueNameAcsr() {
+            return valueNameAcsr;
+        }
+    }
+
+    /**
+     * Models the &lt;property-map&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class PropertyMap extends AbstractModelAction {
+        private final FlexibleStringExpander globalExdr;
+        private final FlexibleMapAccessor<ResourceBundleMapWrapper> mapNameAcsr;
+        private final FlexibleStringExpander resourceExdr;
+
+        public PropertyMap(ModelWidget modelWidget, Element setElement) {
+            super(modelWidget, setElement);
+            this.resourceExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("resource"));
+            this.mapNameAcsr = FlexibleMapAccessor.getInstance(setElement.getAttribute("map-name"));
+            this.globalExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("global"));
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) {
+            String globalStr = this.globalExdr.expandString(context);
+            // default to false
+            boolean global = "true".equals(globalStr);
+            Locale locale = (Locale) context.get("locale");
+            String resource = this.resourceExdr.expandString(context, locale);
+            ResourceBundleMapWrapper existingPropMap = this.mapNameAcsr.get(context);
+            if (existingPropMap == null) {
+                this.mapNameAcsr.put(context, UtilProperties.getResourceBundleMap(resource, locale, context));
+            } else {
+                try {
+                    existingPropMap.addBottomResourceBundle(resource);
+                } catch (IllegalArgumentException e) {
+                    // log the error, but don't let it kill everything just for a typo or bad char in an l10n file
+                    Debug.logError(e, "Error adding resource bundle [" + resource + "]: " + e.toString(), module);
+                }
+            }
+            if (global) {
+                Map<String, Object> globalCtx = UtilGenerics.checkMap(context.get("globalContext"));
+                if (globalCtx != null) {
+                    ResourceBundleMapWrapper globalExistingPropMap = this.mapNameAcsr.get(globalCtx);
+                    if (globalExistingPropMap == null) {
+                        this.mapNameAcsr.put(globalCtx, UtilProperties.getResourceBundleMap(resource, locale, context));
+                    } else {
+                        // is it the same object? if not add it in here too...
+                        if (existingPropMap != globalExistingPropMap) {
+                            try {
+                                globalExistingPropMap.addBottomResourceBundle(resource);
+                            } catch (IllegalArgumentException e) {
+                                // log the error, but don't let it kill everything just for a typo or bad char in an l10n file
+                                Debug.logError(e, "Error adding resource bundle [" + resource + "]: " + e.toString(), module);
+                            }
+                        }
+                    }
+                }
+            }
+        }
+
+        public FlexibleStringExpander getGlobalExdr() {
+            return globalExdr;
+        }
+
+        public FlexibleMapAccessor<ResourceBundleMapWrapper> getMapNameAcsr() {
+            return mapNameAcsr;
+        }
+
+        public FlexibleStringExpander getResourceExdr() {
+            return resourceExdr;
+        }
+    }
+
+    /**
+     * Models the &lt;property-to-field&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class PropertyToField extends AbstractModelAction {
+        private final FlexibleMapAccessor<List<? extends Object>> argListAcsr;
+        private final FlexibleStringExpander defaultExdr;
+        private final FlexibleMapAccessor<Object> fieldAcsr;
+        private final FlexibleStringExpander globalExdr;
+        private final boolean noLocale;
+        private final FlexibleStringExpander propertyExdr;
+        private final FlexibleStringExpander resourceExdr;
+
+        public PropertyToField(ModelWidget modelWidget, Element setElement) {
+            super(modelWidget, setElement);
+            this.resourceExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("resource"));
+            this.propertyExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("property"));
+            this.fieldAcsr = FlexibleMapAccessor.getInstance(setElement.getAttribute("field"));
+            this.defaultExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("default"));
+            this.noLocale = "true".equals(setElement.getAttribute("no-locale"));
+            this.argListAcsr = FlexibleMapAccessor.getInstance(setElement.getAttribute("arg-list-name"));
+            this.globalExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("global"));
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) {
+            //String globalStr = this.globalExdr.expandString(context);
+            // default to false
+            //boolean global = "true".equals(globalStr);
+            Locale locale = (Locale) context.get("locale");
+            String resource = this.resourceExdr.expandString(context, locale);
+            String property = this.propertyExdr.expandString(context, locale);
+            String value = null;
+            if (noLocale) {
+                value = EntityUtilProperties.getPropertyValue(resource, property, WidgetWorker.getDelegator(context));
+            } else {
+                value = EntityUtilProperties.getMessage(resource, property, locale, WidgetWorker.getDelegator(context));
+            }
+            if (UtilValidate.isEmpty(value)) {
+                value = this.defaultExdr.expandString(context);
+            }
+            // note that expanding the value string here will handle defaultValue and the string from
+            //  the properties file; if we decide later that we don't want the string from the properties
+            //  file to be expanded we should just expand the defaultValue at the beginning of this method.
+            value = FlexibleStringExpander.expandString(value, context);
+            if (!argListAcsr.isEmpty()) {
+                List<? extends Object> argList = argListAcsr.get(context);
+                if (UtilValidate.isNotEmpty(argList)) {
+                    value = MessageFormat.format(value, argList.toArray());
+                }
+            }
+            fieldAcsr.put(context, value);
+        }
+
+        public FlexibleMapAccessor<List<? extends Object>> getArgListAcsr() {
+            return argListAcsr;
+        }
+
+        public FlexibleStringExpander getDefaultExdr() {
+            return defaultExdr;
+        }
+
+        public FlexibleMapAccessor<Object> getFieldAcsr() {
+            return fieldAcsr;
+        }
+
+        public FlexibleStringExpander getGlobalExdr() {
+            return globalExdr;
+        }
+
+        public boolean getNoLocale() {
+            return noLocale;
+        }
+
+        public FlexibleStringExpander getPropertyExdr() {
+            return propertyExdr;
+        }
+
+        public FlexibleStringExpander getResourceExdr() {
+            return resourceExdr;
+        }
+    }
+
+    /**
+     * Models the &lt;script&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class Script extends AbstractModelAction {
+        private final String location;
+        private final String method;
+
+        public Script(ModelWidget modelWidget, Element scriptElement) {
+            super(modelWidget, 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);
+        }
+
+        @Override
+        public void runAction(Map<String, Object> context) throws GeneralException {
+            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 GeneralException("Error running simple method at location [" + location + "]", e);
+                }
+            } else {
+                ScriptUtil.executeScript(this.location, this.method, context);
+            }
+        }
+
+        public String getLocation() {
+            return location;
+        }
+
+        public String getMethod() {
+            return method;
+        }
+    }
+
+    /**
+     * Models the &lt;service&gt; element.
+     * 
+     * @see <code>widget-screen.xsd</code>
+     */
+    public static class Service extends AbstractModelAction {
+        private final FlexibleStringExpander autoFieldMapExdr;
+        private final Map<FlexibleMapAccessor<Object>, Object> fieldMap;
+        private final FlexibleMapAccessor<Map<String, Object>> resultMapNameAcsr;
+        private final FlexibleStringExpander serviceNameExdr;
+
+        public Service(ModelWidget modelWidget, Element serviceElement) {
+            super(modelWidget, 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.fieldMap = EntityFinderUtil.makeFieldMap(serviceElement);
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getServiceNameExdr() {
+            return this.serviceNameExdr;
+        }
+
+        @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);
+            try {
+                Map<String, Object> serviceContext = null;
+                if ("true".equals(autoFieldMapString)) {
+                    DispatchContext dc = WidgetWorker.getDispatcher(context).getDispatchContext();
+                    // try a map called "parameters", try it first so values from here are overriden by values in the main context
+                    Map<String, Object> combinedMap = new HashMap<String, Object>();
+                    Map<String, Object> parametersObj = UtilGenerics.toMap(context.get("parameters"));
+                    if (parametersObj != null) {
+                        combinedMap.putAll(parametersObj);
+                    }
+                    combinedMap.putAll(context);
+                    serviceContext = dc.makeValidContext(serviceNameExpanded, ModelService.IN_PARAM, combinedMap);
+                } else if (UtilValidate.isNotEmpty(autoFieldMapString) && !"false".equals(autoFieldMapString)) {
+                    FlexibleMapAccessor<Object> fieldFma = FlexibleMapAccessor.getInstance(autoFieldMapString);
+                    Map<String, Object> autoFieldMap = UtilGenerics.toMap(fieldFma.get(context));
+                    if (autoFieldMap != null) {
+                        serviceContext = WidgetWorker.getDispatcher(context).getDispatchContext()
+                                .makeValidContext(serviceNameExpanded, ModelService.IN_PARAM, autoFieldMap);
+                    }
+                }
+                if (serviceContext == null) {
+                    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);
+                }
+            } catch (GenericServiceException e) {
+                String errMsg = "Error calling service with name " + serviceNameExpanded + ": " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new IllegalArgumentException(errMsg);
+            }
+        }
+
+        public FlexibleStringExpander getAutoFieldMapExdr() {
+            return autoFieldMapExdr;
+        }
+
+        public Map<FlexibleMapAccessor<Object>, Object> getFieldMap() {
+            return fieldMap;
+        }
+
+        public FlexibleMapAccessor<Map<String, Object>> getResultMapNameAcsr() {
+            return resultMapNameAcsr;
+        }
+    }
+
+    /**
+     * Models the &lt;set&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class SetField extends AbstractModelAction {
+        private final FlexibleStringExpander defaultExdr;
+        private final FlexibleMapAccessor<Object> field;
+        private final FlexibleMapAccessor<Object> fromField;
+        private final String fromScope;
+        private final FlexibleStringExpander globalExdr;
+        private final String toScope;
+        private final String type;
+        private final FlexibleStringExpander valueExdr;
+
+        public SetField(ModelWidget modelWidget, Element setElement) {
+            super(modelWidget, setElement);
+            this.field = FlexibleMapAccessor.getInstance(setElement.getAttribute("field"));
+            this.fromField = FlexibleMapAccessor.getInstance(setElement.getAttribute("from-field"));
+            this.valueExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("value"));
+            this.defaultExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("default-value"));
+            this.globalExdr = FlexibleStringExpander.getInstance(setElement.getAttribute("global"));
+            this.type = setElement.getAttribute("type");
+            this.toScope = setElement.getAttribute("to-scope");
+            this.fromScope = setElement.getAttribute("from-scope");
+            if (!this.fromField.isEmpty() && !this.valueExdr.isEmpty()) {
+                throw new IllegalArgumentException("Cannot specify a from-field [" + setElement.getAttribute("from-field")
+                        + "] and a value [" + setElement.getAttribute("value") + "] on the set action in a widget");
+            }
+        }
+
+        @Override
+        public void accept(ModelActionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public Object getInMemoryPersistedFromField(Object storeAgent, Map<String, Object> context) {
+            Object newValue = null;
+            String originalName = this.fromField.getOriginalName();
+            List<String> currentWidgetTrail = UtilGenerics.toList(context.get("_WIDGETTRAIL_"));
+            List<String> trailList = new ArrayList<String>();
+            if (currentWidgetTrail != null) {
+                trailList.addAll(currentWidgetTrail);
+            }
+            for (int i = trailList.size(); i >= 0; i--) {
+                List<String> subTrail = trailList.subList(0, i);
+                String newKey = null;
+                if (subTrail.size() > 0)
+                    newKey = StringUtil.join(subTrail, "|") + "|" + originalName;
+                else
+                    newKey = originalName;
+                if (storeAgent instanceof ServletContext) {
+                    newValue = ((ServletContext) storeAgent).getAttribute(newKey);
+                } else if (storeAgent instanceof HttpSession) {
+                    newValue = ((HttpSession) storeAgent).getAttribute(newKey);
+                }
+                if (newValue != null) {
+                    break;
+                }
+            }
+            return newValue;
+        }
+
+        @SuppressWarnings("rawtypes")
+        @Override
+        public void runAction(Map<String, Object> context) {
+            String globalStr = this.globalExdr.expandString(context);
+            // default to false
+            boolean global = "true".equals(globalStr);
+            Object newValue = null;
+            if (this.fromScope != null && this.fromScope.equals("user")) {
+                if (!this.fromField.isEmpty()) {
+                    HttpSession session = (HttpSession) context.get("session");
+                    newValue = getInMemoryPersistedFromField(session, context);
+                    if (Debug.verboseOn())
+                        Debug.logVerbose("In user getting value for field from [" + this.fromField.getOriginalName() + "]: "
+                                + newValue, module);
+                } else if (!this.valueExdr.isEmpty()) {
+                    newValue = this.valueExdr.expand(context);
+                }
+            } else if (this.fromScope != null && this.fromScope.equals("application")) {
+                if (!this.fromField.isEmpty()) {
+                    ServletContext servletContext = (ServletContext) context.get("application");
+                    newValue = getInMemoryPersistedFromField(servletContext, context);
+                    if (Debug.verboseOn())
+                        Debug.logVerbose("In application getting value for field from [" + this.fromField.getOriginalName()
+                                + "]: " + newValue, module);
+                } else if (!this.valueExdr.isEmpty()) {
+                    newValue = this.valueExdr.expandString(context);
+                }
+            } else {
+                if (!this.fromField.isEmpty()) {
+                    newValue = this.fromField.get(context);
+                    if (Debug.verboseOn())
+                        Debug.logVerbose("Getting value for field from [" + this.fromField.getOriginalName() + "]: " + newValue,
+                                module);
+                } else if (!this.valueExdr.isEmpty()) {
+                    newValue = this.valueExdr.expand(context);
+                }
+            }
+            // If newValue is still empty, use the default value
+            if (ObjectType.isEmpty(newValue) && !this.defaultExdr.isEmpty()) {
+                newValue = this.defaultExdr.expand(context);
+            }
+            if (UtilValidate.isNotEmpty(this.type)) {
+                if ("NewMap".equals(this.type)) {
+                    newValue = new HashMap();
+                } else if ("NewList".equals(this.type)) {
+                    newValue = new LinkedList();
+                } else {
+                    try {
+                        newValue = ObjectType.simpleTypeConvert(newValue, this.type, null, (TimeZone) context.get("timeZone"),
+                                (Locale) context.get("locale"), true);
+                    } catch (GeneralException e) {
+                        String errMsg = "Could not convert field value for the field: [" + this.field.getOriginalName()
+                                + "] to the [" + this.type + "] type for the value [" + newValue + "]: " + e.toString();
+                        Debug.logError(e, errMsg, module);
+                        throw new IllegalArgumentException(errMsg);
+                    }
+                }
+            }
+            if (this.toScope != null && this.toScope.equals("user")) {
+                String originalName = this.field.getOriginalName();
+                List<String> currentWidgetTrail = UtilGenerics.toList(context.get("_WIDGETTRAIL_"));
+                String newKey = "";
+                if (currentWidgetTrail != null) {
+                    newKey = StringUtil.join(currentWidgetTrail, "|");
+                }
+                if (UtilValidate.isNotEmpty(newKey)) {
+                    newKey += "|";
+                }
+                newKey += originalName;
+                HttpSession session = (HttpSession) context.get("session");
+                session.setAttribute(newKey, newValue);
+                if (Debug.verboseOn())
+                    Debug.logVerbose("In user setting value for field from [" + this.field.getOriginalName() + "]: " + newValue,
+                            module);
+            } else if (this.toScope != null && this.toScope.equals("application")) {
+                String originalName = this.field.getOriginalName();
+                List<String> currentWidgetTrail = UtilGenerics.toList(context.get("_WIDGETTRAIL_"));
+                String newKey = "";
+                if (currentWidgetTrail != null) {
+                    newKey = StringUtil.join(currentWidgetTrail, "|");
+                }
+                if (UtilValidate.isNotEmpty(newKey)) {
+                    newKey += "|";
+                }
+                newKey += originalName;
+                ServletContext servletContext = (ServletContext) context.get("application");
+                servletContext.setAttribute(newKey, newValue);
+                if (Debug.verboseOn())
+                    Debug.logVerbose("In application setting value for field from [" + this.field.getOriginalName() + "]: "
+                            + newValue, module);
+            } else {
+                // only do this if it is not global, if global ONLY put it in the global context
+                if (!global) {
+                    if (Debug.verboseOn())
+                        Debug.logVerbose("Setting field [" + this.field.getOriginalName() + "] to value: " + newValue, module);
+                    this.field.put(context, newValue);
+                }
+            }
+            if (global) {
+                Map<String, Object> globalCtx = UtilGenerics.checkMap(context.get("globalContext"));
+                if (globalCtx != null) {
+                    this.field.put(globalCtx, newValue);
+                } else {
+                    this.field.put(context, newValue);
+                }
+            }
+            // this is a hack for backward compatibility with the JPublish page object
+            Map<String, Object> page = UtilGenerics.checkMap(context.get("page"));
+            if (page != null) {
+                this.field.put(page, newValue);
+            }
+        }
+
+        public FlexibleStringExpander getDefaultExdr() {
+            return defaultExdr;
+        }
+
+        public FlexibleMapAccessor<Object> getField() {
+            return field;
+        }
+
+        public FlexibleMapAccessor<Object> getFromField() {
+            return fromField;
+        }
+
+        public String getFromScope() {
+            return fromScope;
+        }
+
+        public FlexibleStringExpander getGlobalExdr() {
+            return globalExdr;
+        }
+
+        public String getToScope() {
+            return toScope;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public FlexibleStringExpander getValueExdr() {
+            return valueExdr;
+        }
+    }
+}

Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelCondition.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelCondition.java?rev=1652638&view=auto
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelCondition.java (added)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/AbstractModelCondition.java Sat Jan 17 16:47:23 2015
@@ -0,0 +1,828 @@
+/*******************************************************************************
+ * 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.ofbiz.widget;
+
+import java.io.Serializable;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.TimeZone;
+
+import org.apache.oro.text.regex.MalformedPatternException;
+import org.apache.oro.text.regex.Pattern;
+import org.apache.oro.text.regex.PatternMatcher;
+import org.apache.oro.text.regex.Perl5Matcher;
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.GeneralException;
+import org.ofbiz.base.util.ObjectType;
+import org.ofbiz.base.util.PatternFactory;
+import org.ofbiz.base.util.UtilGenerics;
+import org.ofbiz.base.util.UtilValidate;
+import org.ofbiz.base.util.UtilXml;
+import org.ofbiz.base.util.collections.FlexibleMapAccessor;
+import org.ofbiz.base.util.string.FlexibleStringExpander;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entityext.permission.EntityPermissionChecker;
+import org.ofbiz.minilang.operation.BaseCompare;
+import org.ofbiz.security.Security;
+import org.ofbiz.service.DispatchContext;
+import org.ofbiz.service.GenericServiceException;
+import org.ofbiz.service.LocalDispatcher;
+import org.ofbiz.service.ModelService;
+import org.ofbiz.service.ServiceUtil;
+import org.ofbiz.widget.xml.*;
+import org.w3c.dom.Element;
+
+/**
+ * Abstract base class for the condition models.
+ */
+@SuppressWarnings("serial")
+public abstract class AbstractModelCondition implements Serializable, ModelCondition {
+
+    /*
+     * ----------------------------------------------------------------------- *
+     *                     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 = AbstractModelCondition.class.getName();
+    public static final ModelConditionFactory DEFAULT_CONDITION_FACTORY = new DefaultConditionFactory();
+
+    public static List<ModelCondition> readSubConditions(ModelConditionFactory factory, ModelWidget modelWidget,
+            Element conditionElement) {
+        List<? extends Element> subElementList = UtilXml.childElementList(conditionElement);
+        List<ModelCondition> condList = new ArrayList<ModelCondition>(subElementList.size());
+        for (Element subElement : subElementList) {
+            condList.add(factory.newInstance(modelWidget, subElement));
+        }
+        return Collections.unmodifiableList(condList);
+    }
+
+    private final ModelWidget modelWidget;
+
+    protected AbstractModelCondition(ModelConditionFactory factory, ModelWidget modelWidget, Element conditionElement) {
+        this.modelWidget = modelWidget;
+    }
+
+    public ModelWidget getModelWidget() {
+        return modelWidget;
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder sb = new StringBuilder();
+        ModelConditionVisitor visitor = new XmlWidgetConditionVisitor(sb);
+        try {
+            accept(visitor);
+        } catch (Exception e) {
+            Debug.logWarning(e, "Exception thrown in XmlWidgetConditionVisitor: ", module);
+        }
+        return sb.toString();
+    }
+
+    /**
+     * Models the &lt;and&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class And extends AbstractModelCondition {
+        private final List<ModelCondition> subConditions;
+
+        private And(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            this.subConditions = readSubConditions(factory, modelWidget, condElement);
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            // return false for the first one in the list that is false, basic and algo
+            for (ModelCondition subCondition : this.subConditions) {
+                if (!subCondition.eval(context)) {
+                    return false;
+                }
+            }
+            return true;
+        }
+
+        public List<ModelCondition> getSubConditions() {
+            return subConditions;
+        }
+    }
+
+    /**
+     * A <code>ModelCondition</code> factory. This factory handles elements
+     * common to all widgets that support conditions. Widgets that have
+     * specialized conditions can extend this class.
+     *
+     */
+    public static class DefaultConditionFactory implements ModelConditionFactory {
+        public static final ModelCondition TRUE = new ModelCondition() {
+            @Override
+            public boolean eval(Map<String, Object> context) {
+                return true;
+            }
+
+            @Override
+            public void accept(ModelConditionVisitor visitor) throws Exception {
+            }
+        };
+
+        public static final ModelCondition FALSE = new ModelCondition() {
+            @Override
+            public boolean eval(Map<String, Object> context) {
+                return false;
+            }
+
+            @Override
+            public void accept(ModelConditionVisitor visitor) throws Exception {
+            }
+        };
+
+        public ModelCondition newInstance(ModelWidget modelWidget, Element conditionElement) {
+            return newInstance(this, modelWidget, conditionElement);
+        }
+
+        // TODO: Test extended factory
+        protected ModelCondition newInstance(ModelConditionFactory factory, ModelWidget modelWidget, Element conditionElement) {
+            if (conditionElement == null) {
+                return TRUE;
+            }
+            String nodeName = conditionElement.getNodeName();
+            if ("and".equals(nodeName)) {
+                return new And(factory, modelWidget, conditionElement);
+            } else if ("xor".equals(nodeName)) {
+                return new Xor(factory, modelWidget, conditionElement);
+            } else if ("or".equals(nodeName)) {
+                return new Or(factory, modelWidget, conditionElement);
+            } else if ("not".equals(nodeName)) {
+                return new Not(factory, modelWidget, conditionElement);
+            } else if ("if-service-permission".equals(nodeName)) {
+                return new IfServicePermission(factory, modelWidget, conditionElement);
+            } else if ("if-has-permission".equals(nodeName)) {
+                return new IfHasPermission(factory, modelWidget, conditionElement);
+            } else if ("if-validate-method".equals(nodeName)) {
+                return new IfValidateMethod(factory, modelWidget, conditionElement);
+            } else if ("if-compare".equals(nodeName)) {
+                return new IfCompare(factory, modelWidget, conditionElement);
+            } else if ("if-compare-field".equals(nodeName)) {
+                return new IfCompareField(factory, modelWidget, conditionElement);
+            } else if ("if-regexp".equals(nodeName)) {
+                return new IfRegexp(factory, modelWidget, conditionElement);
+            } else if ("if-empty".equals(nodeName)) {
+                return new IfEmpty(factory, modelWidget, conditionElement);
+            } else if ("if-entity-permission".equals(nodeName)) {
+                return new IfEntityPermission(factory, modelWidget, conditionElement);
+            } else {
+                throw new IllegalArgumentException("Condition element not supported with name: " + conditionElement.getNodeName());
+            }
+        }
+    }
+
+    /**
+     * Models the &lt;if-compare&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfCompare extends AbstractModelCondition {
+        private final FlexibleMapAccessor<Object> fieldAcsr;
+        private final FlexibleStringExpander formatExdr;
+        private final String operator;
+        private final String type;
+        private final FlexibleStringExpander valueExdr;
+
+        private IfCompare(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            String fieldAcsr = condElement.getAttribute("field");
+            if (fieldAcsr.isEmpty())
+                fieldAcsr = condElement.getAttribute("field-name");
+            this.fieldAcsr = FlexibleMapAccessor.getInstance(fieldAcsr);
+            this.valueExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("value"));
+            this.operator = condElement.getAttribute("operator");
+            this.type = condElement.getAttribute("type");
+            this.formatExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("format"));
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            String value = this.valueExdr.expandString(context);
+            String format = this.formatExdr.expandString(context);
+            Object fieldVal = this.fieldAcsr.get(context);
+            // always use an empty string by default
+            if (fieldVal == null) {
+                fieldVal = "";
+            }
+            List<Object> messages = new LinkedList<Object>();
+            Boolean resultBool = BaseCompare.doRealCompare(fieldVal, value, operator, type, format, messages, null, null, true);
+            if (messages.size() > 0) {
+                messages.add(0, "Error with comparison in if-compare between field [" + fieldAcsr.toString() + "] with value ["
+                        + fieldVal + "] and value [" + value + "] with operator [" + operator + "] and type [" + type + "]: ");
+
+                StringBuilder fullString = new StringBuilder();
+                for (Object item : messages) {
+                    fullString.append(item.toString());
+                }
+                Debug.logWarning(fullString.toString(), module);
+                throw new IllegalArgumentException(fullString.toString());
+            }
+            return resultBool.booleanValue();
+        }
+
+        public FlexibleMapAccessor<Object> getFieldAcsr() {
+            return fieldAcsr;
+        }
+
+        public FlexibleStringExpander getFormatExdr() {
+            return formatExdr;
+        }
+
+        public String getOperator() {
+            return operator;
+        }
+
+        public String getType() {
+            return type;
+        }
+
+        public FlexibleStringExpander getValueExdr() {
+            return valueExdr;
+        }
+    }
+
+    /**
+     * Models the &lt;if-compare-field&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfCompareField extends AbstractModelCondition {
+        private final FlexibleMapAccessor<Object> fieldAcsr;
+        private final FlexibleStringExpander formatExdr;
+        private final String operator;
+        private final FlexibleMapAccessor<Object> toFieldAcsr;
+        private final String type;
+
+        private IfCompareField(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            String fieldAcsr = condElement.getAttribute("field");
+            if (fieldAcsr.isEmpty())
+                fieldAcsr = condElement.getAttribute("field-name");
+            this.fieldAcsr = FlexibleMapAccessor.getInstance(fieldAcsr);
+            String toFieldAcsr = condElement.getAttribute("to-field");
+            if (toFieldAcsr.isEmpty())
+                toFieldAcsr = condElement.getAttribute("to-field-name");
+            this.toFieldAcsr = FlexibleMapAccessor.getInstance(toFieldAcsr);
+            this.operator = condElement.getAttribute("operator");
+            this.type = condElement.getAttribute("type");
+            this.formatExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("format"));
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            String format = this.formatExdr.expandString(context);
+            Object fieldVal = this.fieldAcsr.get(context);
+            Object toFieldVal = this.toFieldAcsr.get(context);
+            // always use an empty string by default
+            if (fieldVal == null) {
+                fieldVal = "";
+            }
+            List<Object> messages = new LinkedList<Object>();
+            Boolean resultBool = BaseCompare.doRealCompare(fieldVal, toFieldVal, operator, type, format, messages, null, null,
+                    false);
+            if (messages.size() > 0) {
+                messages.add(0, "Error with comparison in if-compare-field between field [" + fieldAcsr.toString()
+                        + "] with value [" + fieldVal + "] and to-field [" + toFieldAcsr.toString() + "] with value ["
+                        + toFieldVal + "] with operator [" + operator + "] and type [" + type + "]: ");
+
+                StringBuilder fullString = new StringBuilder();
+                for (Object item : messages) {
+                    fullString.append(item.toString());
+                }
+                Debug.logWarning(fullString.toString(), module);
+                throw new IllegalArgumentException(fullString.toString());
+            }
+            return resultBool.booleanValue();
+        }
+
+        public FlexibleMapAccessor<Object> getFieldAcsr() {
+            return fieldAcsr;
+        }
+
+        public FlexibleStringExpander getFormatExdr() {
+            return formatExdr;
+        }
+
+        public String getOperator() {
+            return operator;
+        }
+
+        public FlexibleMapAccessor<Object> getToFieldAcsr() {
+            return toFieldAcsr;
+        }
+
+        public String getType() {
+            return type;
+        }
+    }
+
+    /**
+     * Models the &lt;if-empty&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfEmpty extends AbstractModelCondition {
+        private final FlexibleMapAccessor<Object> fieldAcsr;
+
+        private IfEmpty(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            String fieldAcsr = condElement.getAttribute("field");
+            if (fieldAcsr.isEmpty())
+                fieldAcsr = condElement.getAttribute("field-name");
+            this.fieldAcsr = FlexibleMapAccessor.getInstance(fieldAcsr);
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            Object fieldVal = this.fieldAcsr.get(context);
+            return ObjectType.isEmpty(fieldVal);
+        }
+
+        public FlexibleMapAccessor<Object> getFieldAcsr() {
+            return fieldAcsr;
+        }
+
+    }
+
+    /**
+     * Models the &lt;if-entity-permission&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfEntityPermission extends AbstractModelCondition {
+        private final EntityPermissionChecker permissionChecker;
+
+        private IfEntityPermission(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            this.permissionChecker = new EntityPermissionChecker(condElement);
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            return permissionChecker.runPermissionCheck(context);
+        }
+
+        public EntityPermissionChecker getPermissionChecker() {
+            return permissionChecker;
+        }
+    }
+
+    /**
+     * Models the &lt;if-has-permission&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfHasPermission extends AbstractModelCondition {
+        private final FlexibleStringExpander actionExdr;
+        private final FlexibleStringExpander permissionExdr;
+
+        private IfHasPermission(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            this.permissionExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("permission"));
+            this.actionExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("action"));
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            // if no user is logged in, treat as if the user does not have permission
+            GenericValue userLogin = (GenericValue) context.get("userLogin");
+            if (userLogin != null) {
+                String permission = permissionExdr.expandString(context);
+                String action = actionExdr.expandString(context);
+                Security security = (Security) context.get("security");
+                if (UtilValidate.isNotEmpty(action)) {
+                    // run hasEntityPermission
+                    if (security.hasEntityPermission(permission, action, userLogin)) {
+                        return true;
+                    }
+                } else {
+                    // run hasPermission
+                    if (security.hasPermission(permission, userLogin)) {
+                        return true;
+                    }
+                }
+            }
+            return false;
+        }
+
+        public FlexibleStringExpander getActionExdr() {
+            return actionExdr;
+        }
+
+        public FlexibleStringExpander getPermissionExdr() {
+            return permissionExdr;
+        }
+    }
+
+    /**
+     * Models the &lt;if-regexp&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfRegexp extends AbstractModelCondition {
+        private final FlexibleStringExpander exprExdr;
+        private final FlexibleMapAccessor<Object> fieldAcsr;
+
+        private IfRegexp(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            String fieldAcsr = condElement.getAttribute("field");
+            if (fieldAcsr.isEmpty())
+                fieldAcsr = condElement.getAttribute("field-name");
+            this.fieldAcsr = FlexibleMapAccessor.getInstance(fieldAcsr);
+            this.exprExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("expr"));
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            Object fieldVal = this.fieldAcsr.get(context);
+            String expr = this.exprExdr.expandString(context);
+            Pattern pattern;
+            try {
+                pattern = PatternFactory.createOrGetPerl5CompiledPattern(expr, true);
+            } catch (MalformedPatternException e) {
+                String errMsg = "Error in evaluation in if-regexp in screen: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new IllegalArgumentException(errMsg);
+            }
+            String fieldString = null;
+            try {
+                fieldString = (String) ObjectType.simpleTypeConvert(fieldVal, "String", null, (TimeZone) context.get("timeZone"),
+                        (Locale) context.get("locale"), true);
+            } catch (GeneralException e) {
+                Debug.logError(e, "Could not convert object to String, using empty String", module);
+            }
+            // always use an empty string by default
+            if (fieldString == null)
+                fieldString = "";
+            PatternMatcher matcher = new Perl5Matcher();
+            return matcher.matches(fieldString, pattern);
+        }
+
+        public FlexibleStringExpander getExprExdr() {
+            return exprExdr;
+        }
+
+        public FlexibleMapAccessor<Object> getFieldAcsr() {
+            return fieldAcsr;
+        }
+    }
+
+    /**
+     * Models the &lt;if-service-permission&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfServicePermission extends AbstractModelCondition {
+        private final FlexibleStringExpander actionExdr;
+        private final FlexibleStringExpander ctxMapExdr;
+        private final FlexibleStringExpander resExdr;
+        private final FlexibleStringExpander serviceExdr;
+
+        private IfServicePermission(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            this.serviceExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("service-name"));
+            this.actionExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("main-action"));
+            this.ctxMapExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("context-map"));
+            this.resExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("resource-description"));
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            // if no user is logged in, treat as if the user does not have permission
+            GenericValue userLogin = (GenericValue) context.get("userLogin");
+            if (userLogin != null) {
+                String serviceName = serviceExdr.expandString(context);
+                String mainAction = actionExdr.expandString(context);
+                String contextMap = ctxMapExdr.expandString(context);
+                String resource = resExdr.expandString(context);
+                if (UtilValidate.isEmpty(resource)) {
+                    resource = serviceName;
+                }
+                if (UtilValidate.isEmpty(serviceName)) {
+                    Debug.logWarning("No permission service-name specified!", module);
+                    return false;
+                }
+                Map<String, Object> serviceContext = UtilGenerics.toMap(context.get(contextMap));
+                if (serviceContext != null) {
+                    // copy the required internal fields
+                    serviceContext.put("userLogin", context.get("userLogin"));
+                    serviceContext.put("locale", context.get("locale"));
+                } else {
+                    serviceContext = context;
+                }
+                // get the service engine objects
+                LocalDispatcher dispatcher = (LocalDispatcher) context.get("dispatcher");
+                DispatchContext dctx = dispatcher.getDispatchContext();
+                // get the service
+                ModelService permService;
+                try {
+                    permService = dctx.getModelService(serviceName);
+                } catch (GenericServiceException e) {
+                    Debug.logError(e, module);
+                    return false;
+                }
+                if (permService != null) {
+                    // build the context
+                    Map<String, Object> svcCtx = permService.makeValid(serviceContext, ModelService.IN_PARAM);
+                    svcCtx.put("resourceDescription", resource);
+                    if (UtilValidate.isNotEmpty(mainAction)) {
+                        svcCtx.put("mainAction", mainAction);
+                    }
+                    // invoke the service
+                    Map<String, Object> resp;
+                    try {
+                        resp = dispatcher.runSync(permService.name, svcCtx, 300, true);
+                    } catch (GenericServiceException e) {
+                        Debug.logError(e, module);
+                        return false;
+                    }
+                    if (ServiceUtil.isError(resp) || ServiceUtil.isFailure(resp)) {
+                        Debug.logError(ServiceUtil.getErrorMessage(resp), module);
+                        return false;
+                    }
+                    Boolean hasPermission = (Boolean) resp.get("hasPermission");
+                    if (hasPermission != null) {
+                        return hasPermission.booleanValue();
+                    }
+                }
+            }
+            return false;
+        }
+
+        public FlexibleStringExpander getActionExdr() {
+            return actionExdr;
+        }
+
+        public FlexibleStringExpander getCtxMapExdr() {
+            return ctxMapExdr;
+        }
+
+        public FlexibleStringExpander getResExdr() {
+            return resExdr;
+        }
+
+        public FlexibleStringExpander getServiceExdr() {
+            return serviceExdr;
+        }
+    }
+
+    /**
+     * Models the &lt;if-validate-method&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class IfValidateMethod extends AbstractModelCondition {
+        private final FlexibleStringExpander classExdr;
+        private final FlexibleMapAccessor<Object> fieldAcsr;
+        private final FlexibleStringExpander methodExdr;
+
+        private IfValidateMethod(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            String fieldAcsr = condElement.getAttribute("field");
+            if (fieldAcsr.isEmpty())
+                fieldAcsr = condElement.getAttribute("field-name");
+            this.fieldAcsr = FlexibleMapAccessor.getInstance(fieldAcsr);
+            this.methodExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("method"));
+            this.classExdr = FlexibleStringExpander.getInstance(condElement.getAttribute("class"));
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            String methodName = this.methodExdr.expandString(context);
+            String className = this.classExdr.expandString(context);
+            Object fieldVal = this.fieldAcsr.get(context);
+            String fieldString = null;
+            if (fieldVal != null) {
+                try {
+                    fieldString = (String) ObjectType.simpleTypeConvert(fieldVal, "String", null,
+                            (TimeZone) context.get("timeZone"), (Locale) context.get("locale"), true);
+                } catch (GeneralException e) {
+                    Debug.logError(e, "Could not convert object to String, using empty String", module);
+                }
+            }
+            // always use an empty string by default
+            if (fieldString == null)
+                fieldString = "";
+            Class<?>[] paramTypes = new Class[] { String.class };
+            Object[] params = new Object[] { fieldString };
+            Class<?> valClass;
+            try {
+                valClass = ObjectType.loadClass(className);
+            } catch (ClassNotFoundException cnfe) {
+                Debug.logError("Could not find validation class: " + className, module);
+                return false;
+            }
+            Method valMethod;
+            try {
+                valMethod = valClass.getMethod(methodName, paramTypes);
+            } catch (NoSuchMethodException cnfe) {
+                Debug.logError("Could not find validation method: " + methodName + " of class " + className, module);
+                return false;
+            }
+            Boolean resultBool = Boolean.FALSE;
+            try {
+                resultBool = (Boolean) valMethod.invoke(null, params);
+            } catch (Exception e) {
+                Debug.logError(e, "Error in IfValidationMethod " + methodName + " of class " + className
+                        + ", defaulting to false ", module);
+            }
+            return resultBool.booleanValue();
+        }
+
+        public FlexibleStringExpander getClassExdr() {
+            return classExdr;
+        }
+
+        public FlexibleMapAccessor<Object> getFieldAcsr() {
+            return fieldAcsr;
+        }
+
+        public FlexibleStringExpander getMethodExdr() {
+            return methodExdr;
+        }
+
+    }
+
+    /**
+     * Models the &lt;not&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class Not extends AbstractModelCondition {
+        private final ModelCondition subCondition;
+
+        private Not(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            Element firstChildElement = UtilXml.firstChildElement(condElement);
+            this.subCondition = factory.newInstance(modelWidget, firstChildElement);
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            return !this.subCondition.eval(context);
+        }
+
+        public ModelCondition getSubCondition() {
+            return subCondition;
+        }
+    }
+
+    /**
+     * Models the &lt;or&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class Or extends AbstractModelCondition {
+        private final List<ModelCondition> subConditions;
+
+        private Or(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            this.subConditions = readSubConditions(factory, modelWidget, condElement);
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            // return true for the first one in the list that is true, basic or algo
+            for (ModelCondition subCondition : this.subConditions) {
+                if (subCondition.eval(context)) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
+        public List<ModelCondition> getSubConditions() {
+            return subConditions;
+        }
+    }
+
+    /**
+     * Models the &lt;xor&gt; element.
+     * 
+     * @see <code>widget-common.xsd</code>
+     */
+    public static class Xor extends AbstractModelCondition {
+        private final List<ModelCondition> subConditions;
+
+        private Xor(ModelConditionFactory factory, ModelWidget modelWidget, Element condElement) {
+            super(factory, modelWidget, condElement);
+            this.subConditions = readSubConditions(factory, modelWidget, condElement);
+        }
+
+        @Override
+        public void accept(ModelConditionVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public boolean eval(Map<String, Object> context) {
+            // if more than one is true stop immediately and return false; if all are false return false; if only one is true return true
+            boolean foundOneTrue = false;
+            for (ModelCondition subCondition : this.subConditions) {
+                if (subCondition.eval(context)) {
+                    if (foundOneTrue) {
+                        // now found two true, so return false
+                        return false;
+                    } else {
+                        foundOneTrue = true;
+                    }
+                }
+            }
+            return foundOneTrue;
+        }
+
+        public List<ModelCondition> getSubConditions() {
+            return subConditions;
+        }
+    }
+}