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

com.tcdng.unify.web.ui.widget.PageManagerImpl Maven / Gradle / Ivy

/*
 * Copyright 2018-2023 The Code Department.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
package com.tcdng.unify.web.ui.widget;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.zip.CRC32;

import com.tcdng.unify.core.AbstractUnifyComponent;
import com.tcdng.unify.core.UnifyCoreErrorConstants;
import com.tcdng.unify.core.UnifyException;
import com.tcdng.unify.core.annotation.Component;
import com.tcdng.unify.core.annotation.Configurable;
import com.tcdng.unify.core.data.FactoryMap;
import com.tcdng.unify.core.data.LocaleFactoryMaps;
import com.tcdng.unify.core.system.SequenceNumberService;
import com.tcdng.unify.core.upl.UplCompiler;
import com.tcdng.unify.core.upl.UplComponent;
import com.tcdng.unify.core.upl.UplDocumentAttributes;
import com.tcdng.unify.core.upl.UplElementAttributes;
import com.tcdng.unify.core.upl.UplElementReferences;
import com.tcdng.unify.core.util.DataUtils;
import com.tcdng.unify.core.util.StringUtils;
import com.tcdng.unify.web.UnifyWebPropertyConstants;
import com.tcdng.unify.web.ui.UnifyWebUIErrorConstants;
import com.tcdng.unify.web.ui.WebUIApplicationComponents;
import com.tcdng.unify.web.ui.widget.panel.StandalonePanel;

/**
 * Default implementation of page manager.
 * 
 * @author The Code Department
 * @since 1.0
 */
@Component(WebUIApplicationComponents.APPLICATION_PAGEMANAGER)
public class PageManagerImpl extends AbstractUnifyComponent implements PageManager {

    private static final String PAGENAME_PREFIX = "p";

    private static final String REMOTE_WEBVIEWER_PAGENAME_PREFIX = "rp";

    @Configurable
    private UplCompiler uplCompiler;

    @Configurable
    private SequenceNumberService sequenceNumberService;

    private PageNameMap pageNameMap;

    private LocaleFactoryMaps standalonePanelInfoByNameMap;

    // Page name property bindings by standalone panel
    private FactoryMap> pageNamePropertyBindings;

    // Expanded component references by component page name
    private Map> expandedReferences;

    // Expanded value references by component page name
    private Map> valueReferences;

    private List documentStyleSheets;

    private List documentScripts;

    private String pageNamePrefix;

    public PageManagerImpl() {
        expandedReferences = new HashMap>();
        valueReferences = new HashMap>();
        documentStyleSheets = Collections.emptyList();
        documentScripts = Collections.emptyList();
        pageNamePrefix = PAGENAME_PREFIX;

        pageNameMap = new PageNameMap();

        standalonePanelInfoByNameMap = new LocaleFactoryMaps()
            {

                @SuppressWarnings("unchecked")
                @Override
                protected StandalonePanelInfo createObject(Locale locale, String name, Object... params)
                        throws Exception {
                    UplDocumentAttributes uplDocumentAttributes = null;
                    if (params.length > 0) {
                        uplDocumentAttributes = (UplDocumentAttributes) params[0];
                    } else {
                        uplDocumentAttributes = uplCompiler.compileComponentDocuments(locale, name);
                    }

                    expandReferences(uplDocumentAttributes, uplDocumentAttributes);

                    // Sort reusable and non-reusable elements and process
                    // reusable ones.
                    Set nonreusableComponentLongNames = new HashSet();
                    Map reusablePageValidations = new LinkedHashMap();
                    Map reusablePageActions = new LinkedHashMap();
                    for (String longName : uplDocumentAttributes.getLongNames()) {
                        UplElementAttributes uea = uplDocumentAttributes.getChildElementByLongName(longName);
                        Class type = (Class) getComponentType(
                                uea.getComponentName());

                        // Only page action and page validation components are
                        // reusable
                        boolean isPageAction = false;
                        if ((isPageAction = PageAction.class.isAssignableFrom(type))
                                || PageValidation.class.isAssignableFrom(type)) {
                            UplComponent uplComponent = getUplComponent(locale, uea.getKey());
                            String pageName = getPageName(longName);

                            if (isPageAction) {
                                PageAction pageAction = (PageAction) uplComponent;
                                pageAction.setId(pageName);
                                expandReferences(uplDocumentAttributes, uea);

                                if (pageAction.isUplAttribute("valueComponentList")) {
                                    if (!valueReferences.containsKey(pageName)) {
                                        UplElementReferences uer = uea.getAttributeValue(UplElementReferences.class,
                                                "valueComponentList");
                                        if (uer != null) {
                                            valueReferences.put(pageName, Collections.unmodifiableList(
                                                    StringUtils.removeDuplicates(getPageNames(uer.getLongNames()))));
                                        }
                                    }
                                }

                                reusablePageActions.put(longName, pageAction);
                            } else {
                                PageValidation pageValidation = (PageValidation) uplComponent;
                                pageValidation.setId(pageName);
                                expandReferences(uplDocumentAttributes, uea);
                                reusablePageValidations.put(longName, pageValidation);
                            }
                        } else {
                        	if (Panel.class.isAssignableFrom(type)) {
                                expandReferences(uplDocumentAttributes, uea);
                        	} else {
                                if (uea.isAttribute("dataComponents")) {
                                    expandReferences(uplDocumentAttributes, uea);
                                }
                        	}

                            nonreusableComponentLongNames.add(longName);
                        }
                    }

                    // Validate page action validations
                    for (Map.Entry entry : reusablePageActions.entrySet()) {
                        PageAction pageAction = entry.getValue();
                        if (pageAction.isUplAttribute("validations")) {
                            UplElementReferences uer = pageAction.getUplAttribute(UplElementReferences.class,
                                    "validations");
                            if (uer != null) {
                                for (String validationLongName : uer.getLongNames()) {
                                    if (reusablePageValidations.get(validationLongName) == null) {
                                        throw new UnifyException(
                                                UnifyWebUIErrorConstants.PAGEACTION_REFERS_UNKNOWN_PAGEVALIDATION,
                                                entry.getKey(), validationLongName);
                                    }
                                }
                            }
                        }
                    }

                    // Process event handlers and create widget info for non
                    // reusable components
                    Map widgetNameInfos = new HashMap();
                    for (String longName : nonreusableComponentLongNames) {
                        UplElementAttributes uea = uplDocumentAttributes.getChildElementByLongName(longName);
                        // Widget info
                        widgetNameInfos.put(longName, createWidgetInfo(uea, nonreusableComponentLongNames));

                        // Go through event handler attributes
                        for (String attribute : uea.getAttributeNames()) {
                            Object value = uea.getAttributeValue(Object.class, attribute);
                            if (value instanceof EventHandler[]) {
                                EventHandler[] eventHandlers = (EventHandler[]) value;
                                for (EventHandler eventHandler : eventHandlers) {
                                    setEventHandlerPageActions(eventHandler, longName, reusablePageActions);
                                }
                            } else if (value instanceof EventHandler) {
                                EventHandler eventHandler = (EventHandler) value;
                                setEventHandlerPageActions(eventHandler, longName, reusablePageActions);
                            }
                        }
                    }

                    widgetNameInfos.put(uplDocumentAttributes.getLongName(),
                            createWidgetInfo(uplDocumentAttributes, nonreusableComponentLongNames));
                    StandalonePanelInfo standalonePanelInfo = new StandalonePanelInfo(
                            Collections.unmodifiableMap(widgetNameInfos),
                            Collections.unmodifiableMap(reusablePageValidations),
                            Collections.unmodifiableMap(reusablePageActions));
                    return standalonePanelInfo;
                }

                private void setEventHandlerPageActions(EventHandler eventHandler, String longName, Map reusablePageActions)
                        throws UnifyException {
                    UplElementReferences uer = eventHandler.getUplAttribute(UplElementReferences.class, "action");
                    if (uer != null) {
                        List pageActionList = new ArrayList();
                        for (String actionLongName : uer.getLongNames()) {
                            PageAction pageAction = reusablePageActions.get(actionLongName);
                            if (pageAction == null) {
                                throw new UnifyException(
                                        UnifyWebUIErrorConstants.EVENTHANDLER_REFERENCING_UNKNOWN_ACTION, longName,
                                        actionLongName);
                            }

                            pageActionList.add(pageAction);
                        }
                        eventHandler.setPageAction(DataUtils.toArray(PageAction.class, pageActionList));
                    }
                }
            };

        pageNamePropertyBindings = new FactoryMap>()
            {

                @Override
                protected Map create(String name, Object... params) throws Exception {
                    Map propertyBindingMap = new HashMap();
                    StandalonePanel sp = createStandalonePanel(Locale.getDefault(), name);
                    for (String longName : sp.getWidgetLongNames()) {
                        Widget widget = sp.getWidgetByLongName(longName);
                        String property = widget.getBinding();
                        if (property != null) {
                            String id = widget.getId();
                            boolean masked = widget.isMasked();

                            // Compute long property
                            StringBuilder longSb = new StringBuilder();
                            longSb.append(property);
                            Widget container = null;
                            while ((container = widget.getContainer()) != null) {
                                String containerProperty = container.getBinding();
                                if (StringUtils.isBlank(containerProperty)) {
                                    break;
                                }

                                longSb.insert(0, '.');
                                longSb.insert(0, containerProperty);
                                widget = container;
                            }

                            String shortProperty = property;
                            int index = property.lastIndexOf('.');
                            if (index >= 0) {
                                shortProperty = property.substring(index + 1);
                            }
                            propertyBindingMap.put(id,
                                    new PropertyInfo(property, shortProperty, longSb.toString(), masked));
                        }
                    }
                    return propertyBindingMap;
                }
            };

    }

    public void setUplCompiler(UplCompiler uplCompiler) {
        this.uplCompiler = uplCompiler;
    }

    public void setSequenceNumberService(SequenceNumberService sequenceNumberService) {
        this.sequenceNumberService = sequenceNumberService;
    }

    @Override
    public List getDocumentStyleSheets() throws UnifyException {
        return documentStyleSheets;
    }

    @Override
    public List getDocumentsScripts() throws UnifyException {
        return documentScripts;
    }

    @Override
    public Page createPage(Locale locale, String name) throws UnifyException {
        return (Page) createStandalonePanel(locale, name);
    }

    @Override
    public boolean invalidateStaleDocument(String name) throws UnifyException {
        if (uplCompiler.invalidateStaleDocument(name)) {
            Set infoList = standalonePanelInfoByNameMap.removeSubkeys(name);
            for (StandalonePanelInfo standalonePanelInfo : infoList) {
                standalonePanelInfo.invalidate();
            }

            pageNamePropertyBindings.remove(name);
            return true;
        }
        return false;
    }

    @Override
    public StandalonePanel createStandalonePanel(Locale locale, String name) throws UnifyException {
        UplDocumentAttributes uplDocumentAttributes = uplCompiler.compileComponentDocuments(locale, name);
        String spLongName = uplDocumentAttributes.getLongName();
        String id = getPageName(spLongName);

        // Construct
        StandalonePanelContext ctx = new StandalonePanelContext(
                standalonePanelInfoByNameMap.get(locale, name, uplDocumentAttributes));
        StandalonePanelInfo standalonePanelInfo = ctx.getStandalonePanelInfo();
        StandalonePanel standalonePanel = (StandalonePanel) getUplComponent(locale, uplDocumentAttributes.getKey());
        standalonePanel.setId(id);
        standalonePanel.setStandalonePanelInfo(standalonePanelInfo);

        // Create non-reusable components
        for (String longName : standalonePanelInfo.getWidgetLongNames()) {
            if (!longName.equals(spLongName)) {
                UplElementAttributes uea = uplDocumentAttributes.getChildElementByLongName(longName);
                // All non reusable components must be widgets
                Widget widget = (Widget) getUplComponent(locale, uea.getKey());
                widget.setId(getPageName(longName));
                ctx.addWidget(widget);
            }
        }

        // Wire widgets
        for (String longName : ctx.getWidgetLongNames()) {
            wireWidget(ctx, ctx.getWidget(longName));
        }

        wireWidget(ctx, standalonePanel);

        // Set container for non-referenced components
        for (String longName : ctx.getWidgetLongNames()) {
            Widget widget = ctx.getWidget(longName);
            if (widget.getContainer() == null) {
                Container parentContainer = (Container) ctx.getWidget(widget.getParentLongName());
                if (parentContainer != null) {
                    widget.setContainer(parentContainer);
                } else {
                    widget.setContainer(standalonePanel);
                }
                ctx.addRepositoryWidget(widget);
            }
        }

        // Perform page on construction
        for (Widget widget : ctx.getWidgets()) {
            widget.onPageConstruct();
        }
        standalonePanel.onPageConstruct();

        return standalonePanel;
    }

    @Override
    public Map getStandalonePanelPropertyBindings(String name) throws UnifyException {
        standalonePanelInfoByNameMap.get(Locale.getDefault(), name);
        return pageNamePropertyBindings.get(name);
    }

    @Override
    public String getPageName(String longName) throws UnifyException {
        return pageNameMap.get(longName);
    }

    @Override
    public List getPageNames(Collection longNames) throws UnifyException {
        if (DataUtils.isNotBlank(longNames)) {
            List resultList = new ArrayList();
            for (String longName : longNames) {
                resultList.add(pageNameMap.get(longName));
            }
            return resultList;
        }
        return Collections.emptyList();
    }

    @Override
    public String getLongName(String pageName) throws UnifyException {
        return pageNameMap.getLongName(pageName);
    }

    @Override
    public List getLongNames(Collection pageNames) throws UnifyException {
        List longNames = new ArrayList();
        for (String pageName : pageNames) {
            longNames.add(pageNameMap.getLongName(pageName));
        }
        return longNames;
    }

    @Override
    public List getExpandedReferences(String pageName) throws UnifyException {
        List resultList = expandedReferences.get(pageName);
        return resultList != null ? resultList : Collections.emptyList();
    }

    @Override
    public List getExpandedReferencesForLongNames(Collection longNames) throws UnifyException {
        if (DataUtils.isNotBlank(longNames)) {
            List resultList = new ArrayList();
            for (String longName : longNames) {
                List list = expandedReferences.get(pageNameMap.get(longName));
                if (DataUtils.isNotBlank(list)) {
                    resultList.addAll(list);
                }
            }

            return resultList;
        }

        return Collections.emptyList();
    }

    @Override
    public List getExpandedReferencesForPageNames(Collection pageNames) throws UnifyException {
        if (DataUtils.isNotBlank(pageNames)) {
            List resultList = new ArrayList();
            for (String pageName : pageNames) {
                List list = expandedReferences.get(pageName);
                if (DataUtils.isNotBlank(list)) {
                    resultList.addAll(list);
                }
            }

            return resultList;
        }
        return Collections.emptyList();
    }

    @Override
    public List getValueReferences(String pageName) throws UnifyException {
        return valueReferences.get(pageName);
    }

    @SuppressWarnings("unchecked")
    @Override
    protected void onInitialize() throws UnifyException {
        if (getContainerSetting(boolean.class, UnifyWebPropertyConstants.APPLICATION_REMOTE_VIEWING_ENABLED, false)) {
            pageNamePrefix = REMOTE_WEBVIEWER_PAGENAME_PREFIX;
        }

        List styleSheets = DataUtils.convert(ArrayList.class, String.class,
                getContainerSetting(Object.class, UnifyWebPropertyConstants.APPLICATION_DOCUMENT_STYLESHEET));
        if (styleSheets != null) {
            List actStyleSheets = new ArrayList();
            for (String styleSheet : styleSheets) {
                actStyleSheets.add(styleSheet);
            }

            documentStyleSheets = Collections.unmodifiableList(actStyleSheets);
        }

        List scripts = DataUtils.convert(ArrayList.class, String.class,
                getContainerSetting(Object.class, UnifyWebPropertyConstants.APPLICATION_DOCUMENT_SCRIPT));
        if (scripts != null) {
            documentScripts = Collections.unmodifiableList(scripts);
        }
    }

    @Override
    protected void onTerminate() throws UnifyException {

    }

    private void wireWidget(StandalonePanelContext ctx, Widget widget) throws UnifyException {
        if (widget instanceof Container) {
            setContainerWidgetRepository(ctx, (Container) widget);
        } else if (widget instanceof MultiControl) {
            MultiControl multiControl = (MultiControl) widget;
            for (String longName : multiControl.getShallowReferencedLongNames("components")) {
                Widget cWidget = ctx.getWidget(longName);
                if (cWidget instanceof Container) {
                    setContainerWidgetRepository(ctx, (Container) cWidget);
                }
                multiControl.addChildWidget((Control) cWidget);
            }
        }
    }

    private void setContainerWidgetRepository(StandalonePanelContext ctx, Container container) throws UnifyException {
        if (!container.hasWidgetRepository()) {
            container.setWidgetRepository(ctx.getWidgetRepository());
            for (String longName : container.getShallowReferencedLongNames("components")) {
                Widget refWidget = ctx.getWidget(longName);
                refWidget.setContainer(container);
                ctx.addRepositoryWidget(refWidget);
            }
        }
    }

    private WidgetNameInfo createWidgetInfo(UplElementAttributes uea, Set widgetLongNames)
            throws UnifyException {
        // Widget info
        Set shallowNames = new HashSet();
        for (String shallowName : uea.getShallowReferencedLongNames()) {
            if (widgetLongNames.contains(shallowName)) {
                shallowNames.add(shallowName);
            }
        }

        if (uea.isAttribute("section")) {
            Section[] sections = uea.getAttributeValue(Section[].class, "section");
            for (Section section : sections) {
                UplElementReferences uplRef = section.getUplAttribute(UplElementReferences.class, "components");
                for (String shallowName : uplRef.getLongNames()) {
                    if (widgetLongNames.contains(shallowName)) {
                        shallowNames.add(shallowName);
                    }
                }
            }
        }

        Set deepNames = new HashSet();
        for (String deepName : uea.getDeepReferencedLongNames()) {
            if (widgetLongNames.contains(deepName)) {
                deepNames.add(deepName);
            }
        }
        return new WidgetNameInfo(Collections.unmodifiableList(new ArrayList(shallowNames)),
                Collections.unmodifiableSet(deepNames));
    }

    private void expandReferences(UplDocumentAttributes uplDocumentAttributes, UplElementAttributes uea)
            throws UnifyException {
        String pageName = getPageName(uea.getLongName());
        if (!expandedReferences.containsKey(pageName)) {
            List expandedList = new ArrayList();
            subExpandReferences(expandedList, uplDocumentAttributes, uea);
            expandedList = StringUtils.removeDuplicates(expandedList);
            expandedList.remove(uea.getLongName());
            expandedReferences.put(pageName, Collections.unmodifiableList(getPageNames(expandedList)));
        }
    }

    @SuppressWarnings("unchecked")
    private void subExpandReferences(List expandedList, UplDocumentAttributes uplDocumentAttributes,
            UplElementAttributes uea) throws UnifyException {
        for (String longName : uea.getShallowReferencedLongNames("components")) {
            subExpandReferences(expandedList, uplDocumentAttributes,
                    uplDocumentAttributes.getChildElementByLongName(longName));
        }

        for (String longName : uea.getShallowReferencedLongNames("dataComponents")) {
            subExpandReferences(expandedList, uplDocumentAttributes,
                    uplDocumentAttributes.getChildElementByLongName(longName));
        }

        if (uea.isAttribute("section")) {
            Section[] sections = uea.getAttributeValue(Section[].class, "section");
            if (sections != null) {
                for (Section section : sections) {
                    UplElementReferences uplRef = section.getUplAttribute(UplElementReferences.class, "components");
                    for (String longName : uplRef.getLongNames()) {
                        subExpandReferences(expandedList, uplDocumentAttributes,
                                uplDocumentAttributes.getChildElementByLongName(longName));
                    }
                }
            }
        }

        Set childElements = uea.getChildElements();
        for (UplElementAttributes ueaInner : childElements) {
            subExpandReferences(expandedList, uplDocumentAttributes, ueaInner);
        }

        if (childElements.isEmpty() && !expandedList.contains(uea.getLongName())) {
            Class type = (Class) getComponentType(
                    uea.getComponentName());
            if (!PageAction.class.isAssignableFrom(type) && !PageValidation.class.isAssignableFrom(type)) {
                expandedList.add(uea.getLongName());
            }
        }
    }

    private class StandalonePanelContext {

        private StandalonePanelInfo standalonePanelInfo;

        private Map componentsByLongNameMap;

        private WidgetRepository widgetRepository;

        public StandalonePanelContext(StandalonePanelInfo standalonePanelInfo) {
            this.standalonePanelInfo = standalonePanelInfo;
            componentsByLongNameMap = new HashMap();
            widgetRepository = new WidgetRepository(standalonePanelInfo.getWidgetInfos());
        }

        public void addWidget(Widget widget) throws UnifyException {
            componentsByLongNameMap.put(widget.getLongName(), widget);
        }

        public Widget getWidget(String longName) {
            return componentsByLongNameMap.get(longName);
        }

        public void addRepositoryWidget(Widget widget) throws UnifyException {
            widgetRepository.putWidget(widget);
        }

        public Collection getWidgetLongNames() {
            return componentsByLongNameMap.keySet();
        }

        public Collection getWidgets() {
            return componentsByLongNameMap.values();
        }

        public StandalonePanelInfo getStandalonePanelInfo() {
            return standalonePanelInfo;
        }

        public WidgetRepository getWidgetRepository() {
            return widgetRepository;
        }

    }

    private class PageNameMap extends FactoryMap {

        private Map longNameByPageNameMap;

        private CRC32 crc = new CRC32();

        public PageNameMap() {
            longNameByPageNameMap = new HashMap();
        }

        public String getLongName(String pageName) throws UnifyException {
            String longName = longNameByPageNameMap.get(pageName);
            if (longName == null) {
                throw new UnifyException(UnifyCoreErrorConstants.UNKNOWN_PAGE_NAME, pageName);
            }
            return longName;
        }

        @Override
        public void clear() {
            super.clear();
            longNameByPageNameMap.clear();
        }

        @Override
        protected String create(String longName, Object... params) throws Exception {
            crc.update(longName.getBytes());
            String pageName = pageNamePrefix + crc.getValue();
            longNameByPageNameMap.put(pageName, longName);
            return pageName;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy