svn commit: r1652852 [10/22] - in /ofbiz/trunk: applications/content/src/org/ofbiz/content/cms/ applications/content/src/org/ofbiz/content/content/ applications/content/src/org/ofbiz/content/data/ applications/content/src/org/ofbiz/content/output/ appl...

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

svn commit: r1652852 [10/22] - in /ofbiz/trunk: applications/content/src/org/ofbiz/content/cms/ applications/content/src/org/ofbiz/content/content/ applications/content/src/org/ofbiz/content/data/ applications/content/src/org/ofbiz/content/output/ appl...

adrianc
Added: ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelScreenWidget.java
URL: http://svn.apache.org/viewvc/ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelScreenWidget.java?rev=1652852&view=auto
==============================================================================
--- ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelScreenWidget.java (added)
+++ ofbiz/trunk/framework/widget/src/org/ofbiz/widget/model/ModelScreenWidget.java Sun Jan 18 21:03:40 2015
@@ -0,0 +1,1957 @@
+/*******************************************************************************
+ * 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.model;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+import java.util.Set;
+
+import javax.xml.parsers.ParserConfigurationException;
+
+import org.ofbiz.base.util.Debug;
+import org.ofbiz.base.util.GeneralException;
+import org.ofbiz.base.util.UtilCodec;
+import org.ofbiz.base.util.UtilGenerics;
+import org.ofbiz.base.util.UtilXml;
+import org.ofbiz.base.util.collections.MapStack;
+import org.ofbiz.base.util.string.FlexibleStringExpander;
+import org.ofbiz.entity.Delegator;
+import org.ofbiz.entity.GenericEntityException;
+import org.ofbiz.entity.GenericValue;
+import org.ofbiz.entity.util.EntityQuery;
+import org.ofbiz.widget.WidgetFactory;
+import org.ofbiz.widget.model.CommonWidgetModels.AutoEntityParameters;
+import org.ofbiz.widget.model.CommonWidgetModels.AutoServiceParameters;
+import org.ofbiz.widget.model.CommonWidgetModels.Image;
+import org.ofbiz.widget.model.CommonWidgetModels.Link;
+import org.ofbiz.widget.model.CommonWidgetModels.Parameter;
+import org.ofbiz.widget.portal.PortalPageWorker;
+import org.ofbiz.widget.renderer.FormRenderer;
+import org.ofbiz.widget.renderer.FormStringRenderer;
+import org.ofbiz.widget.renderer.MenuStringRenderer;
+import org.ofbiz.widget.renderer.ScreenRenderer;
+import org.ofbiz.widget.renderer.ScreenStringRenderer;
+import org.ofbiz.widget.renderer.TreeStringRenderer;
+import org.w3c.dom.Element;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Widget Library - Screen model class
+ */
+@SuppressWarnings("serial")
+public abstract class ModelScreenWidget extends ModelWidget {
+    public static final String module = ModelScreenWidget.class.getName();
+
+    private final ModelScreen modelScreen;
+
+    public ModelScreenWidget(ModelScreen modelScreen, Element widgetElement) {
+        super(widgetElement);
+        this.modelScreen = modelScreen;
+        if (Debug.verboseOn()) Debug.logVerbose("Reading Screen sub-widget with name: " + widgetElement.getNodeName(), module);
+    }
+
+    public abstract void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException;
+
+    protected static List<ModelScreenWidget> readSubWidgets(ModelScreen modelScreen, List<? extends Element> subElementList) {
+        if (subElementList.isEmpty()) {
+            return Collections.emptyList();
+        }
+        List<ModelScreenWidget> subWidgets = new ArrayList<ModelScreenWidget>(subElementList.size());
+        for (Element subElement: subElementList) {
+            subWidgets.add(WidgetFactory.getModelScreenWidget(modelScreen, subElement));
+        }
+        return Collections.unmodifiableList(subWidgets);
+    }
+
+    protected static void renderSubWidgetsString(List<ModelScreenWidget> subWidgets, Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+        if (subWidgets == null) {
+            return;
+        }
+        for (ModelScreenWidget subWidget: subWidgets) {
+            if (Debug.verboseOn()) Debug.logVerbose("Rendering screen " + subWidget.getModelScreen().getName() + "; widget class is " + subWidget.getClass().getName(), module);
+
+            // render the sub-widget itself
+            subWidget.renderWidgetString(writer, context, screenStringRenderer);
+        }
+    }
+
+    public ModelScreen getModelScreen() {
+        return this.modelScreen;
+    }
+
+    public static final class SectionsRenderer implements Map<String, ModelScreenWidget> {
+        private final Map<String, ModelScreenWidget> sectionMap;
+        private final ScreenStringRenderer screenStringRenderer;
+        private final Map<String, Object> context;
+        private final Appendable writer;
+
+        public SectionsRenderer(Map<String, ModelScreenWidget> sectionMap, Map<String, Object> context, Appendable writer,
+                ScreenStringRenderer screenStringRenderer) {
+            Map<String, ModelScreenWidget> localMap = new HashMap<String, ModelScreenWidget>();
+            localMap.putAll(sectionMap);
+            this.sectionMap = Collections.unmodifiableMap(localMap);
+            this.context = context;
+            this.writer = writer;
+            this.screenStringRenderer = screenStringRenderer;
+        }
+
+        /** This is a lot like the ScreenRenderer class and returns an empty String so it can be used more easily with FreeMarker */
+        public String render(String sectionName) throws GeneralException, IOException {
+            ModelScreenWidget section = sectionMap.get(sectionName);
+            // if no section by that name, write nothing
+            if (section != null) {
+                section.renderWidgetString(this.writer, this.context, this.screenStringRenderer);
+            }
+            return "";
+        }
+
+        @Override
+        public int size() {
+            return sectionMap.size();
+        }
+
+        @Override
+        public boolean isEmpty() {
+            return sectionMap.isEmpty();
+        }
+
+        @Override
+        public boolean containsKey(Object key) {
+            return sectionMap.containsKey(key);
+        }
+
+        @Override
+        public boolean containsValue(Object value) {
+            return sectionMap.containsValue(value);
+        }
+
+        @Override
+        public ModelScreenWidget get(Object key) {
+            return sectionMap.get(key);
+        }
+
+        @Override
+        public ModelScreenWidget put(String key, ModelScreenWidget value) {
+            return sectionMap.put(key, value);
+        }
+
+        @Override
+        public ModelScreenWidget remove(Object key) {
+            return sectionMap.remove(key);
+        }
+
+        @Override
+        public void clear() {
+            sectionMap.clear();
+        }
+
+        @Override
+        public Set<String> keySet() {
+            return sectionMap.keySet();
+        }
+
+        @Override
+        public Collection<ModelScreenWidget> values() {
+            return sectionMap.values();
+        }
+
+        @Override
+        public Set<java.util.Map.Entry<String, ModelScreenWidget>> entrySet() {
+            return sectionMap.entrySet();
+        }
+
+        @Override
+        public boolean equals(Object o) {
+            return sectionMap.equals(o);
+        }
+
+        @Override
+        public int hashCode() {
+            return sectionMap.hashCode();
+        }
+
+        @Override
+        public void putAll(Map<? extends String, ? extends ModelScreenWidget> m) {
+            sectionMap.putAll(m);
+        }
+    }
+
+    public static final class Section extends ModelScreenWidget {
+        public static final String TAG_NAME = "section";
+        private final ModelCondition condition;
+        private final List<ModelAction> actions;
+        private final List<ModelScreenWidget> subWidgets;
+        private final List<ModelScreenWidget> failWidgets;
+        private final boolean isMainSection;
+
+        public Section(ModelScreen modelScreen, Element sectionElement) {
+            this(modelScreen, sectionElement, false);
+        }
+
+        public Section(ModelScreen modelScreen, Element sectionElement, boolean isMainSection) {
+            super(modelScreen, sectionElement);
+
+            // read condition under the "condition" element
+            Element conditionElement = UtilXml.firstChildElement(sectionElement, "condition");
+            if (conditionElement != null) {
+                conditionElement = UtilXml.firstChildElement(conditionElement);
+                this.condition = ModelScreenCondition.SCREEN_CONDITION_FACTORY.newInstance(modelScreen, conditionElement);
+            } else {
+                this.condition = null;
+            }
+
+            // read all actions under the "actions" element
+            Element actionsElement = UtilXml.firstChildElement(sectionElement, "actions");
+            if (actionsElement != null) {
+                this.actions = AbstractModelAction.readSubActions(modelScreen, actionsElement);
+            } else {
+                this.actions = Collections.emptyList();
+            }
+
+            // read sub-widgets
+            Element widgetsElement = UtilXml.firstChildElement(sectionElement, "widgets");
+            if (widgetsElement != null) {
+                List<? extends Element> subElementList = UtilXml.childElementList(widgetsElement);
+                this.subWidgets = ModelScreenWidget.readSubWidgets(getModelScreen(), subElementList);
+            } else {
+                this.subWidgets = Collections.emptyList();
+            }
+
+            // read fail-widgets
+            Element failWidgetsElement = UtilXml.firstChildElement(sectionElement, "fail-widgets");
+            if (failWidgetsElement != null) {
+                List<? extends Element> failElementList = UtilXml.childElementList(failWidgetsElement);
+                this.failWidgets = ModelScreenWidget.readSubWidgets(getModelScreen(), failElementList);
+            } else {
+                this.failWidgets = Collections.emptyList();
+            }
+            this.isMainSection = isMainSection;
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            // check the condition, if there is one
+            boolean condTrue = true;
+            if (this.condition != null) {
+                if (!this.condition.eval(context)) {
+                    condTrue = false;
+                }
+            }
+
+            // if condition does not exist or evals to true run actions and render widgets, otherwise render fail-widgets
+            if (condTrue) {
+                // run the actions only if true
+                AbstractModelAction.runSubActions(this.actions, context);
+
+                try {
+                    // section by definition do not themselves do anything, so this method will generally do nothing, but we'll call it anyway
+                    screenStringRenderer.renderSectionBegin(writer, context, this);
+
+                    // render sub-widgets
+                    renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer);
+
+                    screenStringRenderer.renderSectionEnd(writer, context, this);
+                } catch (IOException e) {
+                    String errMsg = "Error rendering widgets section [" + getName() + "] in screen named [" + getModelScreen().getName() + "]: " + e.toString();
+                    Debug.logError(e, errMsg, module);
+                    throw new RuntimeException(errMsg);
+                }
+            } else {
+                try {
+                    // section by definition do not themselves do anything, so this method will generally do nothing, but we'll call it anyway
+                    screenStringRenderer.renderSectionBegin(writer, context, this);
+
+                    // render sub-widgets
+                    renderSubWidgetsString(this.failWidgets, writer, context, screenStringRenderer);
+
+                    screenStringRenderer.renderSectionEnd(writer, context, this);
+                } catch (IOException e) {
+                    String errMsg = "Error rendering fail-widgets section [" + this.getName() + "] in screen named [" + getModelScreen().getName() + "]: " + e.toString();
+                    Debug.logError(e, errMsg, module);
+                    throw new RuntimeException(errMsg);
+                }
+            }
+
+        }
+
+        @Override
+        public String getBoundaryCommentName() {
+            if (isMainSection) {
+                return getModelScreen().getSourceLocation() + "#" + getModelScreen().getName();
+            } else {
+                return getName();
+            }
+        }
+
+        public List<ModelAction> getActions() {
+            return actions;
+        }
+
+        public List<ModelScreenWidget> getSubWidgets() {
+            return subWidgets;
+        }
+
+        public List<ModelScreenWidget> getFailWidgets() {
+            return failWidgets;
+        }
+
+        public boolean isMainSection() {
+            return isMainSection;
+        }
+
+        public ModelCondition getCondition() {
+            return condition;
+        }
+    }
+
+    public static final class ColumnContainer extends ModelScreenWidget {
+        public static final String TAG_NAME = "column-container";
+        private final FlexibleStringExpander idExdr;
+        private final FlexibleStringExpander styleExdr;
+        private final List<Column> columns;
+
+        public ColumnContainer(ModelScreen modelScreen, Element containerElement) {
+            super(modelScreen, containerElement);
+            this.idExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("id"));
+            this.styleExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("style"));
+            List<? extends Element> subElementList = UtilXml.childElementList(containerElement, "column");
+            List<Column> columns = new ArrayList<Column>(subElementList.size());
+            for (Element element : subElementList) {
+                columns.add(new Column(modelScreen, element));
+            }
+            this.columns = Collections.unmodifiableList(columns);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            try {
+                screenStringRenderer.renderColumnContainer(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering container in screen named [" + getModelScreen().getName() + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+        }
+
+        public List<Column> getColumns() {
+            return this.columns;
+        }
+
+        public String getId(Map<String, Object> context) {
+            return this.idExdr.expandString(context);
+        }
+
+        public String getStyle(Map<String, Object> context) {
+            return this.styleExdr.expandString(context);
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return idExdr;
+        }
+
+        public FlexibleStringExpander getStyleExdr() {
+            return styleExdr;
+        }
+    }
+
+    public static final class Column extends ModelWidget {
+        private final FlexibleStringExpander idExdr;
+        private final FlexibleStringExpander styleExdr;
+        private final List<ModelScreenWidget> subWidgets;
+
+        public Column(ModelScreen modelScreen, Element columnElement) {
+            super(columnElement);
+            this.idExdr = FlexibleStringExpander.getInstance(columnElement.getAttribute("id"));
+            this.styleExdr = FlexibleStringExpander.getInstance(columnElement.getAttribute("style"));
+            List<? extends Element> subElementList = UtilXml.childElementList(columnElement);
+            this.subWidgets = Collections.unmodifiableList(readSubWidgets(modelScreen, subElementList));
+        }
+
+        public List<ModelScreenWidget> getSubWidgets() {
+            return this.subWidgets;
+        }
+
+        public String getId(Map<String, Object> context) {
+            return this.idExdr.expandString(context);
+        }
+
+        public String getStyle(Map<String, Object> context) {
+            return this.styleExdr.expandString(context);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return idExdr;
+        }
+
+        public FlexibleStringExpander getStyleExdr() {
+            return styleExdr;
+        }
+    }
+
+    public static final class Container extends ModelScreenWidget {
+        public static final String TAG_NAME = "container";
+        private final FlexibleStringExpander idExdr;
+        private final FlexibleStringExpander styleExdr;
+        private final FlexibleStringExpander autoUpdateTargetExdr;
+        private final FlexibleStringExpander autoUpdateInterval;
+        private final List<ModelScreenWidget> subWidgets;
+
+        public Container(ModelScreen modelScreen, Element containerElement) {
+            super(modelScreen, containerElement);
+            this.idExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("id"));
+            this.styleExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("style"));
+            this.autoUpdateTargetExdr = FlexibleStringExpander.getInstance(containerElement.getAttribute("auto-update-target"));
+            String autoUpdateInterval = containerElement.getAttribute("auto-update-interval");
+            if (autoUpdateInterval.isEmpty()) {
+                autoUpdateInterval = "2";
+            }
+            this.autoUpdateInterval = FlexibleStringExpander.getInstance(autoUpdateInterval);
+            // read sub-widgets
+            List<? extends Element> subElementList = UtilXml.childElementList(containerElement);
+            this.subWidgets = ModelScreenWidget.readSubWidgets(getModelScreen(), subElementList);
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            try {
+                screenStringRenderer.renderContainerBegin(writer, context, this);
+
+                // render sub-widgets
+                renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer);
+
+                screenStringRenderer.renderContainerEnd(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering container in screen named [" + getModelScreen().getName() + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+        }
+
+        public String getId(Map<String, Object> context) {
+            return this.idExdr.expandString(context);
+        }
+
+        public String getStyle(Map<String, Object> context) {
+            return this.styleExdr.expandString(context);
+        }
+
+        public String getAutoUpdateTargetExdr(Map<String, Object> context) {
+            return this.autoUpdateTargetExdr.expandString(context);
+        }
+
+        public String getAutoUpdateInterval(Map<String, Object> context) {
+            return this.autoUpdateInterval.expandString(context);
+        }
+
+        public List<ModelScreenWidget> getSubWidgets() {
+            return subWidgets;
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return idExdr;
+        }
+
+        public FlexibleStringExpander getStyleExdr() {
+            return styleExdr;
+        }
+
+        public FlexibleStringExpander getAutoUpdateTargetExdr() {
+            return autoUpdateTargetExdr;
+        }
+
+        public FlexibleStringExpander getAutoUpdateInterval() {
+            return autoUpdateInterval;
+        }
+    }
+
+    public static final class Screenlet extends ModelScreenWidget {
+        public static final String TAG_NAME = "screenlet";
+        private final FlexibleStringExpander idExdr;
+        private final FlexibleStringExpander titleExdr;
+        private final Menu navigationMenu;
+        private final Menu tabMenu;
+        private final Form navigationForm;
+        private final boolean collapsible;
+        private final FlexibleStringExpander initiallyCollapsed;
+        private final boolean saveCollapsed;
+        private final boolean padded;
+        private final List<ModelScreenWidget> subWidgets;
+
+        public Screenlet(ModelScreen modelScreen, Element screenletElement) {
+            super(modelScreen, screenletElement);
+            this.idExdr = FlexibleStringExpander.getInstance(screenletElement.getAttribute("id"));
+            boolean collapsible = "true".equals(screenletElement.getAttribute("collapsible"));
+            this.initiallyCollapsed = FlexibleStringExpander.getInstance(screenletElement.getAttribute("initially-collapsed"));
+            if ("true".equals(this.initiallyCollapsed.getOriginal())) {
+                collapsible = true;
+            }
+            this.collapsible = collapsible;
+            // By default, for a collapsible screenlet, the collapsed/expanded status must be saved
+            this.saveCollapsed = !("false".equals(screenletElement.getAttribute("save-collapsed")));
+
+            boolean padded = !"false".equals(screenletElement.getAttribute("padded"));
+            if (this.collapsible && getName().isEmpty() && idExdr.isEmpty()) {
+                throw new IllegalArgumentException("Collapsible screenlets must have a name or id [" + getModelScreen().getName() + "]");
+            }
+            this.titleExdr = FlexibleStringExpander.getInstance(screenletElement.getAttribute("title"));
+            List<? extends Element> subElementList = UtilXml.childElementList(screenletElement);
+            // Make a copy of the unmodifiable List so we can modify it.
+            ArrayList<ModelScreenWidget> subWidgets = new ArrayList<ModelScreenWidget>(ModelScreenWidget.readSubWidgets(getModelScreen(), subElementList));
+            Menu navigationMenu = null;
+            String navMenuName = screenletElement.getAttribute("navigation-menu-name");
+            if (!navMenuName.isEmpty()) {
+                for (ModelWidget subWidget : subWidgets) {
+                    if (navMenuName.equals(subWidget.getName()) && subWidget instanceof Menu) {
+                        navigationMenu = (Menu) subWidget;
+                        subWidgets.remove(subWidget);
+                        break;
+                    }
+                }
+            }
+            this.navigationMenu = navigationMenu;
+            Menu tabMenu = null;
+            String tabMenuName = screenletElement.getAttribute("tab-menu-name");
+            if (!tabMenuName.isEmpty()) {
+                for (ModelWidget subWidget : subWidgets) {
+                    if (tabMenuName.equals(subWidget.getName()) && subWidget instanceof Menu) {
+                        tabMenu = (Menu) subWidget;
+                        subWidgets.remove(subWidget);
+                        break;
+                    }
+                }
+            }
+            this.tabMenu = tabMenu;
+            Form navigationForm = null;
+            String formName = screenletElement.getAttribute("navigation-form-name");
+            if (!formName.isEmpty() && this.navigationMenu == null) {
+                for (ModelWidget subWidget : subWidgets) {
+                    if (formName.equals(subWidget.getName()) && subWidget instanceof Form) {
+                        navigationForm = (Form) subWidget;
+                        padded = false;
+                        break;
+                    }
+                }
+            }
+            this.subWidgets = Collections.unmodifiableList(subWidgets);
+            this.navigationForm = navigationForm;
+            this.padded = padded;
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            boolean collapsed = getInitiallyCollapsed(context);
+            if (this.collapsible) {
+                String preferenceKey = getPreferenceKey(context) + "_collapsed";
+                Map<String, Object> requestParameters = UtilGenerics.checkMap(context.get("requestParameters"));
+                if (requestParameters != null) {
+                    String collapsedParam = (String) requestParameters.get(preferenceKey);
+                    collapsed = "true".equals(collapsedParam);
+                }
+            }
+            try {
+                screenStringRenderer.renderScreenletBegin(writer, context, collapsed, this);
+                for (ModelScreenWidget subWidget : this.subWidgets) {
+                    screenStringRenderer.renderScreenletSubWidget(writer, context, subWidget, this);
+                }
+                screenStringRenderer.renderScreenletEnd(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering screenlet in screen named [" + getModelScreen().getName() + "]: ";
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg + e);
+            }
+        }
+
+        public boolean collapsible() {
+            return this.collapsible;
+        }
+
+        //initially-collapsed status, which may be overriden by user preference
+        public boolean getInitiallyCollapsed(Map<String, Object> context) {
+            String screenletId = this.getId(context) + "_collapsed";
+            Map<String, ? extends Object> userPreferences = UtilGenerics.checkMap(context.get("userPreferences"));
+            if (userPreferences != null && userPreferences.containsKey(screenletId)) {
+                return Boolean.valueOf((String)userPreferences.get(screenletId)).booleanValue() ;
+            }
+
+            return "true".equals(this.initiallyCollapsed.expand(context));
+        }
+
+        public boolean saveCollapsed() {
+            return this.saveCollapsed;
+        }
+        public boolean padded() {
+            return this.padded;
+        }
+
+        public String getPreferenceKey(Map<String, Object> context) {
+            String name = this.idExdr.expandString(context);
+            if (name.isEmpty()) {
+                name = "screenlet" + "_" + Integer.toHexString(hashCode());
+            }
+            return name;
+        }
+
+        public String getId(Map<String, Object> context) {
+            return this.idExdr.expandString(context);
+        }
+
+        public List<ModelScreenWidget> getSubWidgets() {
+            return subWidgets;
+        }
+
+        public String getTitle(Map<String, Object> context) {
+            String title = this.titleExdr.expandString(context);
+            UtilCodec.SimpleEncoder simpleEncoder = (UtilCodec.SimpleEncoder) context.get("simpleEncoder");
+            if (simpleEncoder != null) {
+                title = simpleEncoder.encode(title);
+            }
+            return title;
+        }
+
+        public Menu getNavigationMenu() {
+            return this.navigationMenu;
+        }
+
+        public Form getNavigationForm() {
+            return this.navigationForm;
+        }
+
+        public Menu getTabMenu() {
+            return this.tabMenu;
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return idExdr;
+        }
+
+        public FlexibleStringExpander getTitleExdr() {
+            return titleExdr;
+        }
+
+        public boolean getCollapsible() {
+            return collapsible;
+        }
+
+        public FlexibleStringExpander getInitiallyCollapsed() {
+            return initiallyCollapsed;
+        }
+
+        public boolean getSaveCollapsed() {
+            return saveCollapsed;
+        }
+
+        public boolean getPadded() {
+            return padded;
+        }
+    }
+
+    public static final class HorizontalSeparator extends ModelScreenWidget {
+        public static final String TAG_NAME = "horizontal-separator";
+        private final FlexibleStringExpander idExdr;
+        private final FlexibleStringExpander styleExdr;
+
+        public HorizontalSeparator(ModelScreen modelScreen, Element separatorElement) {
+            super(modelScreen, separatorElement);
+            this.idExdr = FlexibleStringExpander.getInstance(separatorElement.getAttribute("id"));
+            this.styleExdr = FlexibleStringExpander.getInstance(separatorElement.getAttribute("style"));
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            screenStringRenderer.renderHorizontalSeparator(writer, context, this);
+        }
+
+        public String getId(Map<String, Object> context) {
+            return this.idExdr.expandString(context);
+        }
+
+        public String getStyle(Map<String, Object> context) {
+            return this.styleExdr.expandString(context);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return idExdr;
+        }
+
+        public FlexibleStringExpander getStyleExdr() {
+            return styleExdr;
+        }
+    }
+
+    public static final class IncludeScreen extends ModelScreenWidget {
+        public static final String TAG_NAME = "include-screen";
+        private final FlexibleStringExpander nameExdr;
+        private final FlexibleStringExpander locationExdr;
+        private final FlexibleStringExpander shareScopeExdr;
+
+        public IncludeScreen(ModelScreen modelScreen, Element includeScreenElement) {
+            super(modelScreen, includeScreenElement);
+            this.nameExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("name"));
+            this.locationExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("location"));
+            this.shareScopeExdr = FlexibleStringExpander.getInstance(includeScreenElement.getAttribute("share-scope"));
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            // if we are not sharing the scope, protect it using the MapStack
+            boolean protectScope = !shareScope(context);
+            if (protectScope) {
+                if (!(context instanceof MapStack<?>)) {
+                    context = MapStack.create(context);
+                }
+
+                UtilGenerics.<MapStack<String>>cast(context).push();
+
+                // build the widgetpath
+                List<String> widgetTrail = UtilGenerics.toList(context.get("_WIDGETTRAIL_"));
+                if (widgetTrail == null) {
+                    widgetTrail = new LinkedList<String>();
+                }
+
+                String thisName = nameExdr.expandString(context);
+                widgetTrail.add(thisName);
+                context.put("_WIDGETTRAIL_", widgetTrail);
+            }
+
+            // don't need the renderer here, will just pass this on down to another screen call; screenStringRenderer.renderContainerBegin(writer, context, this);
+            String name = this.getName(context);
+            String location = this.getLocation(context);
+
+            if (name.isEmpty()) {
+                if (Debug.verboseOn()) Debug.logVerbose("In the include-screen tag the screen name was empty, ignoring include; in screen [" + getModelScreen().getName() + "]", module);
+                return;
+            }
+
+            ScreenFactory.renderReferencedScreen(name, location, this, writer, context, screenStringRenderer);
+
+            if (protectScope) {
+                UtilGenerics.<MapStack<String>>cast(context).pop();
+            }
+        }
+
+        public String getName(Map<String, Object> context) {
+            return this.nameExdr.expandString(context);
+        }
+
+        public String getLocation(Map<String, Object> context) {
+            return this.locationExdr.expandString(context);
+        }
+
+        public boolean shareScope(Map<String, Object> context) {
+            String shareScopeString = this.shareScopeExdr.expandString(context);
+            // defaults to false, so anything but true is false
+            return "true".equals(shareScopeString);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getNameExdr() {
+            return nameExdr;
+        }
+
+        public FlexibleStringExpander getLocationExdr() {
+            return locationExdr;
+        }
+
+        public FlexibleStringExpander getShareScopeExdr() {
+            return shareScopeExdr;
+        }
+    }
+
+    public static final class DecoratorScreen extends ModelScreenWidget {
+        public static final String TAG_NAME = "decorator-screen";
+        private final FlexibleStringExpander nameExdr;
+        private final FlexibleStringExpander locationExdr;
+        private final Map<String, ModelScreenWidget> sectionMap;
+
+        public DecoratorScreen(ModelScreen modelScreen, Element decoratorScreenElement) {
+            super(modelScreen, decoratorScreenElement);
+            this.nameExdr = FlexibleStringExpander.getInstance(decoratorScreenElement.getAttribute("name"));
+            this.locationExdr = FlexibleStringExpander.getInstance(decoratorScreenElement.getAttribute("location"));
+            Map<String, ModelScreenWidget> sectionMap = new HashMap<String, ModelScreenWidget>();
+            List<? extends Element> decoratorSectionElementList = UtilXml.childElementList(decoratorScreenElement, "decorator-section");
+            for (Element decoratorSectionElement: decoratorSectionElementList) {
+                DecoratorSection decoratorSection = new DecoratorSection(modelScreen, decoratorSectionElement);
+                sectionMap.put(decoratorSection.getName(), decoratorSection);
+            }
+            this.sectionMap = Collections.unmodifiableMap(sectionMap);
+        }
+
+        @Override
+        @SuppressWarnings("unchecked")
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            // isolate the scope
+            if (!(context instanceof MapStack)) {
+                context = MapStack.create(context);
+            }
+
+            MapStack contextMs = (MapStack) context;
+
+            // create a standAloneStack, basically a "save point" for this SectionsRenderer, and make a new "screens" object just for it so it is isolated and doesn't follow the stack down
+            MapStack standAloneStack = contextMs.standAloneChildStack();
+            standAloneStack.put("screens", new ScreenRenderer(writer, standAloneStack, screenStringRenderer));
+            SectionsRenderer sections = new SectionsRenderer(this.sectionMap, standAloneStack, writer, screenStringRenderer);
+
+            // put the sectionMap in the context, make sure it is in the sub-scope, ie after calling push on the MapStack
+            contextMs.push();
+            context.put("sections", sections);
+
+            String name = this.getName(context);
+            String location = this.getLocation(context);
+
+            ScreenFactory.renderReferencedScreen(name, location, this, writer, context, screenStringRenderer);
+
+            contextMs.pop();
+        }
+
+        public String getName(Map<String, Object> context) {
+            return this.nameExdr.expandString(context);
+        }
+
+        public String getLocation(Map<String, Object> context) {
+            return this.locationExdr.expandString(context);
+        }
+
+        public Map<String, ModelScreenWidget> getSectionMap() {
+            return sectionMap;
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getNameExdr() {
+            return nameExdr;
+        }
+
+        public FlexibleStringExpander getLocationExdr() {
+            return locationExdr;
+        }
+
+    }
+
+    public static final class DecoratorSection extends ModelScreenWidget {
+        public static final String TAG_NAME = "decorator-section";
+        private final List<ModelScreenWidget> subWidgets;
+
+        public DecoratorSection(ModelScreen modelScreen, Element decoratorSectionElement) {
+            super(modelScreen, decoratorSectionElement);
+            // read sub-widgets
+            List<? extends Element> subElementList = UtilXml.childElementList(decoratorSectionElement);
+            this.subWidgets = ModelScreenWidget.readSubWidgets(getModelScreen(), subElementList);
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            // render sub-widgets
+            renderSubWidgetsString(this.subWidgets, writer, context, screenStringRenderer);
+        }
+
+        public List<ModelScreenWidget> getSubWidgets() {
+            return subWidgets;
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+    }
+
+    public static final class DecoratorSectionInclude extends ModelScreenWidget {
+        public static final String TAG_NAME = "decorator-section-include";
+
+        public DecoratorSectionInclude(ModelScreen modelScreen, Element decoratorSectionElement) {
+            super(modelScreen, decoratorSectionElement);
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            Map<String, ? extends Object> preRenderedContent = UtilGenerics.checkMap(context.get("preRenderedContent"));
+            if (preRenderedContent != null && preRenderedContent.containsKey(getName())) {
+                try {
+                    writer.append((String) preRenderedContent.get(getName()));
+                } catch (IOException e) {
+                    String errMsg = "Error rendering pre-rendered content in screen named [" + getModelScreen().getName() + "]: " + e.toString();
+                    Debug.logError(e, errMsg, module);
+                    throw new RuntimeException(errMsg);
+                }
+            } else {
+                SectionsRenderer sections = (SectionsRenderer) context.get("sections");
+                // for now if sections is null, just log a warning; may be permissible to make the screen for flexible
+                if (sections == null) {
+                    Debug.logWarning("In decorator-section-include could not find sections object in the context, not rendering section with name [" + getName() + "]", module);
+                } else {
+                    sections.render(getName());
+                }
+            }
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+    }
+
+    public static final class Label extends ModelScreenWidget {
+        public static final String TAG_NAME = "label";
+        private final FlexibleStringExpander textExdr;
+        private final FlexibleStringExpander idExdr;
+        private final FlexibleStringExpander styleExdr;
+
+        public Label(ModelScreen modelScreen, Element labelElement) {
+            super(modelScreen, labelElement);
+
+            // put the text attribute first, then the pcdata under the element, if both are there of course
+            String textAttr = labelElement.getAttribute("text");
+            String pcdata = UtilXml.elementValue(labelElement);
+            if (pcdata == null) {
+                pcdata = "";
+            }
+            this.textExdr = FlexibleStringExpander.getInstance(textAttr + pcdata);
+
+            this.idExdr = FlexibleStringExpander.getInstance(labelElement.getAttribute("id"));
+            this.styleExdr = FlexibleStringExpander.getInstance(labelElement.getAttribute("style"));
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
+            try {
+                screenStringRenderer.renderLabel(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering label in screen named [" + getModelScreen().getName() + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+        }
+
+        public String getText(Map<String, Object> context) {
+            String text = this.textExdr.expandString(context);
+            // FIXME: Encoding should be done by the renderer, not by the model.
+            UtilCodec.SimpleEncoder simpleEncoder = (UtilCodec.SimpleEncoder) context.get("simpleEncoder");
+            if (simpleEncoder != null) {
+                text = simpleEncoder.encode(text);
+            }
+            return text;
+        }
+
+        public String getId(Map<String, Object> context) {
+            return this.idExdr.expandString(context);
+        }
+
+        public String getStyle(Map<String, Object> context) {
+            return this.styleExdr.expandString(context);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getTextExdr() {
+            return textExdr;
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return idExdr;
+        }
+
+        public FlexibleStringExpander getStyleExdr() {
+            return styleExdr;
+        }
+    }
+
+    public static final class Form extends ModelScreenWidget {
+        public static final String TAG_NAME = "include-form";
+        private final FlexibleStringExpander nameExdr;
+        private final FlexibleStringExpander locationExdr;
+        private final FlexibleStringExpander shareScopeExdr;
+
+        public Form(ModelScreen modelScreen, Element formElement) {
+            super(modelScreen, formElement);
+            this.nameExdr = FlexibleStringExpander.getInstance(formElement.getAttribute("name"));
+            this.locationExdr = FlexibleStringExpander.getInstance(formElement.getAttribute("location"));
+            this.shareScopeExdr = FlexibleStringExpander.getInstance(formElement.getAttribute("share-scope"));
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
+            // Output format might not support forms, so make form rendering optional.
+            FormStringRenderer formStringRenderer = (FormStringRenderer) context.get("formStringRenderer");
+            if (formStringRenderer == null) {
+                Debug.logVerbose("FormStringRenderer instance not found in rendering context, form not rendered.", module);
+                return;
+            }
+            boolean protectScope = !shareScope(context);
+            if (protectScope) {
+                if (!(context instanceof MapStack<?>)) {
+                    context = MapStack.create(context);
+                }
+                UtilGenerics.<MapStack<String>>cast(context).push();
+            }
+            ModelForm modelForm = getModelForm(context);
+            FormRenderer renderer = new FormRenderer(modelForm, formStringRenderer);
+            try {
+                renderer.render(writer, context);
+            } catch (Exception e) {
+                String errMsg = "Error rendering included form named [" + getName() + "] at location [" + this.getLocation(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg + e);
+            }
+
+            if (protectScope) {
+                UtilGenerics.<MapStack<String>>cast(context).pop();
+            }
+        }
+
+        public ModelForm getModelForm(Map<String, Object> context) {
+            ModelForm modelForm = null;
+            String name = this.getName(context);
+            String location = this.getLocation(context);
+            try {
+                modelForm = FormFactory.getFormFromLocation(location, name, getModelScreen().getDelegator(context).getModelReader(), getModelScreen().getDispatcher(context).getDispatchContext());
+            } catch (Exception e) {
+                String errMsg = "Error rendering included form named [" + name + "] at location [" + location + "]: ";
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg + e);
+            }
+            return modelForm;
+        }
+
+        public String getName(Map<String, Object> context) {
+            return this.nameExdr.expandString(context);
+        }
+
+        public String getLocation() {
+            return locationExdr.getOriginal();
+        }
+
+        public String getLocation(Map<String, Object> context) {
+            return this.locationExdr.expandString(context);
+        }
+
+        public boolean shareScope(Map<String, Object> context) {
+            String shareScopeString = this.shareScopeExdr.expandString(context);
+            // defaults to false, so anything but true is false
+            return "true".equals(shareScopeString);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getNameExdr() {
+            return nameExdr;
+        }
+
+        public FlexibleStringExpander getLocationExdr() {
+            return locationExdr;
+        }
+
+        public FlexibleStringExpander getShareScopeExdr() {
+            return shareScopeExdr;
+        }
+
+    }
+
+    public static final class Tree extends ModelScreenWidget {
+        public static final String TAG_NAME = "include-tree";
+        private final FlexibleStringExpander nameExdr;
+        private final FlexibleStringExpander locationExdr;
+        private final FlexibleStringExpander shareScopeExdr;
+
+        public Tree(ModelScreen modelScreen, Element treeElement) {
+            super(modelScreen, treeElement);
+            this.nameExdr = FlexibleStringExpander.getInstance(treeElement.getAttribute("name"));
+            this.locationExdr = FlexibleStringExpander.getInstance(treeElement.getAttribute("location"));
+            this.shareScopeExdr = FlexibleStringExpander.getInstance(treeElement.getAttribute("share-scope"));
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            // Output format might not support trees, so make tree rendering optional.
+            TreeStringRenderer treeStringRenderer = (TreeStringRenderer) context.get("treeStringRenderer");
+            if (treeStringRenderer == null) {
+                Debug.logVerbose("TreeStringRenderer instance not found in rendering context, tree not rendered.", module);
+                return;
+            }
+            boolean protectScope = !shareScope(context);
+            if (protectScope) {
+                if (!(context instanceof MapStack<?>)) {
+                    context = MapStack.create(context);
+                }
+                UtilGenerics.<MapStack<String>>cast(context).push();
+            }
+
+            String name = this.getName(context);
+            String location = this.getLocation(context);
+            ModelTree modelTree = null;
+            try {
+                modelTree = TreeFactory.getTreeFromLocation(this.getLocation(context), this.getName(context), getModelScreen().getDelegator(context), getModelScreen().getDispatcher(context));
+            } catch (IOException e) {
+                String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            } catch (SAXException e) {
+                String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            } catch (ParserConfigurationException e) {
+                String errMsg = "Error rendering included tree named [" + name + "] at location [" + location + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+            modelTree.renderTreeString(writer, context, treeStringRenderer);
+            if (protectScope) {
+                UtilGenerics.<MapStack<String>>cast(context).pop();
+            }
+        }
+
+        public String getName(Map<String, Object> context) {
+            return this.nameExdr.expandString(context);
+        }
+
+        public String getLocation(Map<String, Object> context) {
+            return this.locationExdr.expandString(context);
+        }
+
+        public boolean shareScope(Map<String, Object> context) {
+            String shareScopeString = this.shareScopeExdr.expandString(context);
+            // defaults to false, so anything but true is false
+            return "true".equals(shareScopeString);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getLocationExdr() {
+            return locationExdr;
+        }
+
+        public FlexibleStringExpander getShareScopeExdr() {
+            return shareScopeExdr;
+        }
+    }
+
+    public static final class PlatformSpecific extends ModelScreenWidget {
+        public static final String TAG_NAME = "platform-specific";
+        private final Map<String, ModelScreenWidget> subWidgets;
+
+        public PlatformSpecific(ModelScreen modelScreen, Element platformSpecificElement) {
+            super(modelScreen, platformSpecificElement);
+            Map<String, ModelScreenWidget> subWidgets = new HashMap<String, ModelScreenWidget>();
+            List<? extends Element> childElements = UtilXml.childElementList(platformSpecificElement);
+            if (childElements != null) {
+                for (Element childElement: childElements) {
+                    if ("html".equals(childElement.getNodeName())) {
+                        subWidgets.put("html", new HtmlWidget(modelScreen, childElement));
+                    } else if ("xsl-fo".equals(childElement.getNodeName())) {
+                        subWidgets.put("xsl-fo", new HtmlWidget(modelScreen, childElement));
+                    } else if ("xml".equals(childElement.getNodeName())) {
+                        subWidgets.put("xml", new HtmlWidget(modelScreen, childElement));
+                    } else {
+                        throw new IllegalArgumentException("Tag not supported under the platform-specific tag with name: " + childElement.getNodeName());
+                    }
+                }
+            }
+            this.subWidgets = Collections.unmodifiableMap(subWidgets);
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            ModelScreenWidget subWidget = null;
+            subWidget = subWidgets.get(screenStringRenderer.getRendererName());
+            if (subWidget == null) {
+                // This is here for backward compatibility
+                Debug.logWarning("In platform-dependent could not find template for " + screenStringRenderer.getRendererName() + ", using the one for html.", module);
+                subWidget = subWidgets.get("html");
+            }
+            if (subWidget != null) {
+                subWidget.renderWidgetString(writer, context, screenStringRenderer);
+            }
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public Map<String, ModelScreenWidget> getSubWidgets() {
+            return subWidgets;
+        }
+    }
+
+    public static final class Content extends ModelScreenWidget {
+        public static final String TAG_NAME = "content";
+
+        private final FlexibleStringExpander contentId;
+        private final FlexibleStringExpander editRequest;
+        private final FlexibleStringExpander editContainerStyle;
+        private final FlexibleStringExpander enableEditName;
+        private final boolean xmlEscape;
+        private final FlexibleStringExpander dataResourceId;
+        private final String width;
+        private final String height;
+        private final String border;
+
+        public Content(ModelScreen modelScreen, Element subContentElement) {
+            super(modelScreen, subContentElement);
+            this.contentId = FlexibleStringExpander.getInstance(subContentElement.getAttribute("content-id"));
+            this.dataResourceId = FlexibleStringExpander.getInstance(subContentElement.getAttribute("dataresource-id"));
+            this.editRequest = FlexibleStringExpander.getInstance(subContentElement.getAttribute("edit-request"));
+            this.editContainerStyle = FlexibleStringExpander.getInstance(subContentElement.getAttribute("edit-container-style"));
+            this.enableEditName = FlexibleStringExpander.getInstance(subContentElement.getAttribute("enable-edit-name"));
+            this.xmlEscape = "true".equals(subContentElement.getAttribute("xml-escape"));
+            String width = subContentElement.getAttribute("width");
+            if (width.isEmpty())
+                width = "60%";
+            this.height = subContentElement.getAttribute("height");
+            if (this.height.isEmpty())
+                width = "400px";
+            this.width = width;
+            this.border = subContentElement.getAttribute("border");
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
+            try {
+                // pushing the contentId on the context as "contentId" is done
+                // because many times there will be embedded "subcontent" elements
+                // that use the syntax: <subcontent content-id="${contentId}"...
+                // and this is a step to make sure that it is there.
+                Delegator delegator = (Delegator) context.get("delegator");
+                GenericValue content = null;
+                String expandedDataResourceId = getDataResourceId(context);
+                String expandedContentId = getContentId(context);
+                if (!(context instanceof MapStack<?>)) {
+                    context = MapStack.create(context);
+                }
+
+                // This is an important step to make sure that the current contentId is in the context
+                // as templates that contain "subcontent" elements will expect to find the master
+                // contentId in the context as "contentId".
+                UtilGenerics.<MapStack<String>>cast(context).push();
+                context.put("contentId", expandedContentId);
+
+                if (expandedDataResourceId.isEmpty()) {
+                    if (!expandedContentId.isEmpty()) {
+                        content = EntityQuery.use(delegator).from("Content").where("contentId", expandedContentId).cache().queryOne();
+                    } else {
+                        String errMsg = "contentId is empty.";
+                        Debug.logError(errMsg, module);
+                        return;
+                    }
+                    if (content != null) {
+                        expandedDataResourceId = content.getString("dataResourceId");
+                    } else {
+                        String errMsg = "Could not find content with contentId [" + expandedContentId + "] ";
+                        Debug.logError(errMsg, module);
+                        throw new RuntimeException(errMsg);
+                    }
+                }
+
+                GenericValue dataResource = null;
+                if (!expandedDataResourceId.isEmpty()) {
+                    dataResource = EntityQuery.use(delegator).from("DataResource").where("dataResourceId", expandedDataResourceId).cache().queryOne();
+                }
+
+                String mimeTypeId = null;
+                if (dataResource != null) {
+                    mimeTypeId = dataResource.getString("mimeTypeId");
+                }
+                if (content != null) {
+                    mimeTypeId = content.getString("mimeTypeId");
+                }
+
+                if (!(mimeTypeId != null
+                        && ((mimeTypeId.indexOf("application") >= 0) || (mimeTypeId.indexOf("image")) >= 0))) {
+                    screenStringRenderer.renderContentBegin(writer, context, this);
+                    screenStringRenderer.renderContentBody(writer, context, this);
+                    screenStringRenderer.renderContentEnd(writer, context, this);
+                }
+                UtilGenerics.<MapStack<String>>cast(context).pop();
+            } catch (IOException e) {
+                String errMsg = "Error rendering content with contentId [" + getContentId(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            } catch (GenericEntityException e) {
+                String errMsg = "Error obtaining content with contentId [" + getContentId(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+
+        }
+
+        public String getContentId(Map<String, Object> context) {
+            return this.contentId.expandString(context);
+        }
+
+        public String getDataResourceId() {
+            return this.dataResourceId.getOriginal();
+        }
+
+        public String getDataResourceId(Map<String, Object> context) {
+            return this.dataResourceId.expandString(context);
+        }
+
+        public String getEditRequest(Map<String, Object> context) {
+            return this.editRequest.expandString(context);
+        }
+
+        public String getEditContainerStyle(Map<String, Object> context) {
+            return this.editContainerStyle.expandString(context);
+        }
+
+        public String getEnableEditName(Map<String, Object> context) {
+            return this.enableEditName.expandString(context);
+        }
+
+        public boolean xmlEscape() {
+            return this.xmlEscape;
+        }
+
+        public String getWidth() {
+            return this.width;
+        }
+
+        public String getHeight() {
+            return this.height;
+        }
+
+        public String getBorder() {
+            return this.border;
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getContentId() {
+            return contentId;
+        }
+
+        public FlexibleStringExpander getEditRequest() {
+            return editRequest;
+        }
+
+        public FlexibleStringExpander getEditContainerStyle() {
+            return editContainerStyle;
+        }
+
+        public FlexibleStringExpander getEnableEditName() {
+            return enableEditName;
+        }
+    }
+
+    public static final class SubContent extends ModelScreenWidget {
+        public static final String TAG_NAME = "sub-content";
+        private final FlexibleStringExpander contentId;
+        private final FlexibleStringExpander mapKey;
+        private final FlexibleStringExpander editRequest;
+        private final FlexibleStringExpander editContainerStyle;
+        private final FlexibleStringExpander enableEditName;
+        private final boolean xmlEscape;
+
+        public SubContent(ModelScreen modelScreen, Element subContentElement) {
+            super(modelScreen, subContentElement);
+            this.contentId = FlexibleStringExpander.getInstance(subContentElement.getAttribute("content-id"));
+            String mapKey = subContentElement.getAttribute("map-key");
+            if (mapKey.isEmpty()) {
+                mapKey = subContentElement.getAttribute("assoc-name");
+            }
+            this.mapKey = FlexibleStringExpander.getInstance(mapKey);
+            this.editRequest = FlexibleStringExpander.getInstance(subContentElement.getAttribute("edit-request"));
+            this.editContainerStyle = FlexibleStringExpander.getInstance(subContentElement.getAttribute("edit-container-style"));
+            this.enableEditName = FlexibleStringExpander.getInstance(subContentElement.getAttribute("enable-edit-name"));
+            this.xmlEscape = "true".equals(subContentElement.getAttribute("xml-escape"));
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
+            try {
+                screenStringRenderer.renderSubContentBegin(writer, context, this);
+                screenStringRenderer.renderSubContentBody(writer, context, this);
+                screenStringRenderer.renderSubContentEnd(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering subContent with contentId [" + getContentId(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+        }
+
+        public String getContentId(Map<String, Object> context) {
+            return this.contentId.expandString(context);
+        }
+
+        public String getMapKey(Map<String, Object> context) {
+            return this.mapKey.expandString(context);
+        }
+
+        public String getEditRequest(Map<String, Object> context) {
+            return this.editRequest.expandString(context);
+        }
+
+        public String getEditContainerStyle(Map<String, Object> context) {
+            return this.editContainerStyle.expandString(context);
+        }
+
+        public String getEnableEditName(Map<String, Object> context) {
+            return this.enableEditName.expandString(context);
+        }
+
+        public boolean xmlEscape() {
+            return this.xmlEscape;
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            // TODO Auto-generated method stub
+            
+        }
+    }
+
+    public static final class Menu extends ModelScreenWidget {
+        public static final String TAG_NAME = "include-menu";
+        private final FlexibleStringExpander nameExdr;
+        private final FlexibleStringExpander locationExdr;
+
+        public Menu(ModelScreen modelScreen, Element menuElement) {
+            super(modelScreen, menuElement);
+            this.nameExdr = FlexibleStringExpander.getInstance(menuElement.getAttribute("name"));
+            this.locationExdr = FlexibleStringExpander.getInstance(menuElement.getAttribute("location"));
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws IOException {
+            // Output format might not support menus, so make menu rendering optional.
+            MenuStringRenderer menuStringRenderer = (MenuStringRenderer) context.get("menuStringRenderer");
+            if (menuStringRenderer == null) {
+                Debug.logVerbose("MenuStringRenderer instance not found in rendering context, menu not rendered.", module);
+                return;
+            }
+            ModelMenu modelMenu = getModelMenu(context);
+            modelMenu.renderMenuString(writer, context, menuStringRenderer);
+        }
+
+        public ModelMenu getModelMenu(Map<String, Object> context) {
+            String name = this.getName(context);
+            String location = this.getLocation(context);
+            ModelMenu modelMenu = null;
+            try {
+                modelMenu = MenuFactory.getMenuFromLocation(location, name);
+            } catch (Exception e) {
+                String errMsg = "Error rendering included menu named [" + name + "] at location [" + location + "]: ";
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg + e);
+            }
+            return modelMenu;
+        }
+
+        public String getName(Map<String, Object> context) {
+            return this.nameExdr.expandString(context);
+        }
+
+        public String getLocation(Map<String, Object> context) {
+            return this.locationExdr.expandString(context);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getLocationExdr() {
+            return locationExdr;
+        }
+    }
+
+    public static final class ScreenLink extends ModelScreenWidget {
+        public static final String TAG_NAME = "link";
+        private final Link link;
+        private final ScreenImage image;
+
+        public ScreenLink(ModelScreen modelScreen, Element linkElement) {
+            super(modelScreen, linkElement);
+            this.link = new Link(linkElement);
+            Element imageElement = UtilXml.firstChildElement(linkElement, "image");
+            if (imageElement != null) {
+                this.image = new ScreenImage(modelScreen, imageElement);
+            } else {
+                this.image = null;
+            }
+        }
+
+        public String getName() {
+            return link.getName();
+        }
+
+        public String getText(Map<String, Object> context) {
+            return link.getText(context);
+        }
+
+        public String getId(Map<String, Object> context) {
+            return link.getId(context);
+        }
+
+        public String getStyle(Map<String, Object> context) {
+            return link.getStyle(context);
+        }
+
+        public String getTarget(Map<String, Object> context) {
+            return link.getTarget(context);
+        }
+
+        public String getName(Map<String, Object> context) {
+            return link.getName(context);
+        }
+
+        public String getTargetWindow(Map<String, Object> context) {
+            return link.getTargetWindow(context);
+        }
+
+        public String getUrlMode() {
+            return link.getUrlMode();
+        }
+
+        public String getPrefix(Map<String, Object> context) {
+            return link.getPrefix(context);
+        }
+
+        public boolean getFullPath() {
+            return link.getFullPath();
+        }
+
+        public boolean getSecure() {
+            return link.getSecure();
+        }
+
+        public boolean getEncode() {
+            return link.getEncode();
+        }
+
+        public ScreenImage getImage() {
+            return image;
+        }
+
+        public String getLinkType() {
+            return link.getLinkType();
+        }
+
+        public String getWidth() {
+            return link.getWidth();
+        }
+
+        public String getHeight() {
+            return link.getHeight();
+        }
+
+        public Map<String, String> getParameterMap(Map<String, Object> context) {
+            return link.getParameterMap(context);
+        }
+
+        public FlexibleStringExpander getTextExdr() {
+            return link.getTextExdr();
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return link.getIdExdr();
+        }
+
+        public FlexibleStringExpander getStyleExdr() {
+            return link.getStyleExdr();
+        }
+
+        public FlexibleStringExpander getTargetExdr() {
+            return link.getTargetExdr();
+        }
+
+        public FlexibleStringExpander getTargetWindowExdr() {
+            return link.getTargetWindowExdr();
+        }
+
+        public FlexibleStringExpander getPrefixExdr() {
+            return link.getPrefixExdr();
+        }
+
+        public FlexibleStringExpander getNameExdr() {
+            return link.getNameExdr();
+        }
+
+        public List<Parameter> getParameterList() {
+            return link.getParameterList();
+        }
+
+        public AutoServiceParameters getAutoServiceParameters() {
+            return link.getAutoServiceParameters();
+        }
+
+        public AutoEntityParameters getAutoEntityParameters() {
+            return link.getAutoEntityParameters();
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
+            try {
+                screenStringRenderer.renderLink(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering link with id [" + link.getId(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public Link getLink() {
+            return link;
+        }
+    }
+
+    public static final class ScreenImage extends ModelScreenWidget {
+        public static final String TAG_NAME = "image";
+        private final Image image;
+
+        public String getName() {
+            return image.getName();
+        }
+
+        public String getSrc(Map<String, Object> context) {
+            return image.getSrc(context);
+        }
+
+        public String getId(Map<String, Object> context) {
+            return image.getId(context);
+        }
+
+        public String getStyle(Map<String, Object> context) {
+            return image.getStyle(context);
+        }
+
+        public String getWidth(Map<String, Object> context) {
+            return image.getWidth(context);
+        }
+
+        public String getHeight(Map<String, Object> context) {
+            return image.getHeight(context);
+        }
+
+        public String getBorder(Map<String, Object> context) {
+            return image.getBorder(context);
+        }
+
+        public String getAlt(Map<String, Object> context) {
+            return image.getAlt(context);
+        }
+
+        public String getUrlMode() {
+            return image.getUrlMode();
+        }
+
+        public FlexibleStringExpander getSrcExdr() {
+            return image.getSrcExdr();
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return image.getIdExdr();
+        }
+
+        public FlexibleStringExpander getStyleExdr() {
+            return image.getStyleExdr();
+        }
+
+        public FlexibleStringExpander getWidthExdr() {
+            return image.getWidthExdr();
+        }
+
+        public FlexibleStringExpander getHeightExdr() {
+            return image.getHeightExdr();
+        }
+
+        public FlexibleStringExpander getBorderExdr() {
+            return image.getBorderExdr();
+        }
+
+        public FlexibleStringExpander getAlt() {
+            return image.getAlt();
+        }
+
+        public ScreenImage(ModelScreen modelScreen, Element imageElement) {
+            super(modelScreen, imageElement);
+            this.image = new Image(imageElement);
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) {
+            try {
+                screenStringRenderer.renderImage(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering image with id [" + image.getId(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public Image getImage() {
+            return image;
+        }
+    }
+
+    public static final class PortalPage extends ModelScreenWidget {
+        public static final String TAG_NAME = "include-portal-page";
+        private final FlexibleStringExpander idExdr;
+        private final FlexibleStringExpander confModeExdr;
+        private final Boolean usePrivate;
+
+        public PortalPage(ModelScreen modelScreen, Element portalPageElement) {
+            super(modelScreen, portalPageElement);
+            this.idExdr = FlexibleStringExpander.getInstance(portalPageElement.getAttribute("id"));
+            this.confModeExdr = FlexibleStringExpander.getInstance(portalPageElement.getAttribute("conf-mode"));
+            this.usePrivate = !("false".equals(portalPageElement.getAttribute("use-private")));
+        }
+
+        private GenericValue getPortalPageValue(Map<String, Object> context) {
+            Delegator delegator = (Delegator) context.get("delegator");
+            String expandedPortalPageId = getId(context);
+            GenericValue portalPage = null;
+            if (!expandedPortalPageId.isEmpty()) {
+                if (usePrivate) {
+                    portalPage = PortalPageWorker.getPortalPage(expandedPortalPageId, context);
+                } else {
+                    try {
+                        portalPage = EntityQuery.use(delegator)
+                                                .from("PortalPage")
+                                                .where("portalPageId", expandedPortalPageId)
+                                                .cache().queryOne();
+                    } catch (GenericEntityException e) {
+                        throw new RuntimeException(e);
+                    }
+                }
+            }
+            if (portalPage == null) {
+                String errMsg = "Could not find PortalPage with portalPageId [" + expandedPortalPageId + "] ";
+                Debug.logError(errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+            return portalPage;
+        }
+
+        @Override
+        public void renderWidgetString(Appendable writer, Map<String, Object> context, ScreenStringRenderer screenStringRenderer) throws GeneralException, IOException {
+            try {
+                Delegator delegator = (Delegator) context.get("delegator");
+                List<GenericValue> portalPageColumns = null;
+                List<GenericValue> portalPagePortlets = null;
+                List<GenericValue> portletAttributes = null;
+                GenericValue portalPage = getPortalPageValue(context);
+                String actualPortalPageId = portalPage.getString("portalPageId");
+                portalPageColumns = EntityQuery.use(delegator)
+                                               .from("PortalPageColumn")
+                                               .where("portalPageId", actualPortalPageId)
+                                               .orderBy("columnSeqId")
+                                               .cache(true)
+                                               .queryList();
+                
+                // Renders the portalPage header
+                screenStringRenderer.renderPortalPageBegin(writer, context, this);
+                
+                // First column has no previous column
+                String prevColumnSeqId = "";
+                
+                // Iterates through the PortalPage columns
+                ListIterator <GenericValue>columnsIterator = portalPageColumns.listIterator();
+                while(columnsIterator.hasNext()) {
+                    GenericValue columnValue = columnsIterator.next();
+                    String columnSeqId = columnValue.getString("columnSeqId");
+                    
+                    // Renders the portalPageColumn header
+                    screenStringRenderer.renderPortalPageColumnBegin(writer, context, this, columnValue);
+
+                    // Get the Portlets located in the current column
+                    portalPagePortlets = EntityQuery.use(delegator)
+                                                    .from("PortalPagePortletView")
+                                                    .where("portalPageId", portalPage.getString("portalPageId"), "columnSeqId", columnSeqId)
+                                                    .orderBy("sequenceNum")
+                                                    .queryList();
+                    // First Portlet in a Column has no previous Portlet
+                    String prevPortletId = "";
+                    String prevPortletSeqId = "";
+
+                    // If this is not the last column, get the next columnSeqId
+                    String nextColumnSeqId = "";
+                    if (columnsIterator.hasNext()) {
+                        nextColumnSeqId = portalPageColumns.get(columnsIterator.nextIndex()).getString("columnSeqId");
+                    }
+                    
+                    // Iterates through the Portlets in the Column
+                    ListIterator <GenericValue>portletsIterator = portalPagePortlets.listIterator();
+                    while(portletsIterator.hasNext()) {
+                        GenericValue portletValue = portletsIterator.next();
+
+                        // If not the last portlet in the column, get the next nextPortletId and nextPortletSeqId
+                        String nextPortletId = "";
+                        String nextPortletSeqId = "";
+                        if (portletsIterator.hasNext()) {
+                            nextPortletId = portalPagePortlets.get(portletsIterator.nextIndex()).getString("portalPortletId");
+                            nextPortletSeqId = portalPagePortlets.get(portletsIterator.nextIndex()).getString("portletSeqId");
+                        }
+
+                        // Set info to allow portlet movement in the page
+                        context.put("prevPortletId", prevPortletId);
+                        context.put("prevPortletSeqId", prevPortletSeqId);
+                        context.put("nextPortletId", nextPortletId);
+                        context.put("nextPortletSeqId", nextPortletSeqId);
+                        context.put("prevColumnSeqId", prevColumnSeqId);
+                        context.put("nextColumnSeqId", nextColumnSeqId);
+                      
+                        // Get portlet's attributes
+                        portletAttributes = EntityQuery.use(delegator)
+                                                       .from("PortletAttribute")
+                                                       .where("portalPageId", portletValue.get("portalPageId"), "portalPortletId", portletValue.get("portalPortletId"), "portletSeqId", portletValue.get("portletSeqId"))
+                                                       .queryList();
+                        
+                        ListIterator <GenericValue>attributesIterator = portletAttributes.listIterator();
+                        while (attributesIterator.hasNext()) {
+                            GenericValue attribute = attributesIterator.next();
+                            context.put(attribute.getString("attrName"), attribute.getString("attrValue"));
+                        }
+                        
+                        // Renders the portalPagePortlet
+                        screenStringRenderer.renderPortalPagePortletBegin(writer, context, this, portletValue);
+                        screenStringRenderer.renderPortalPagePortletBody(writer, context, this, portletValue);
+                        screenStringRenderer.renderPortalPagePortletEnd(writer, context, this, portletValue);
+
+                        // Remove the portlet's attributes so that these are not available for other portlets
+                        while (attributesIterator.hasPrevious()) {
+                            GenericValue attribute = attributesIterator.previous();
+                            context.remove(attribute.getString("attrName"));
+                        }
+                        
+                        // Uses the actual portlet as prevPortlet for next iteration
+                        prevPortletId = (String) portletValue.get("portalPortletId");
+                        prevPortletSeqId = (String) portletValue.get("portletSeqId");
+                    }
+                    // Renders the portalPageColumn footer
+                    screenStringRenderer.renderPortalPageColumnEnd(writer, context, this, columnValue);
+
+                    // Uses the actual columnSeqId as prevColumnSeqId for next iteration
+                    prevColumnSeqId = columnSeqId;
+                }
+                // Renders the portalPage footer
+                screenStringRenderer.renderPortalPageEnd(writer, context, this);
+            } catch (IOException e) {
+                String errMsg = "Error rendering PortalPage with portalPageId [" + getId(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            } catch (GenericEntityException e) {
+                String errMsg = "Error obtaining PortalPage with portalPageId [" + getId(context) + "]: " + e.toString();
+                Debug.logError(e, errMsg, module);
+                throw new RuntimeException(errMsg);
+            }
+        }
+
+        public String getId(Map<String, Object> context) {
+            return this.idExdr.expandString(context);
+        }
+
+        public String getOriginalPortalPageId(Map<String, Object> context) {
+            GenericValue portalPage = getPortalPageValue(context);
+            return portalPage.getString("originalPortalPageId");
+        }
+        
+        public String getActualPortalPageId(Map<String, Object> context) {
+            GenericValue portalPage = getPortalPageValue(context);
+            return portalPage.getString("portalPageId");
+        }
+
+        public String getConfMode(Map<String, Object> context) {
+            return this.confModeExdr.expandString(context);
+        }
+
+        public String getUsePrivate() {
+            return Boolean.toString(this.usePrivate);
+        }
+
+        @Override
+        public void accept(ModelWidgetVisitor visitor) throws Exception {
+            visitor.visit(this);
+        }
+
+        public FlexibleStringExpander getIdExdr() {
+            return idExdr;
+        }
+
+        public FlexibleStringExpander getConfModeExdr() {
+            return confModeExdr;
+        }
+    }
+
+}