com.haulmont.cuba.gui.config.WindowConfig.java Source code

Java tutorial

Introduction

Here is the source code for com.haulmont.cuba.gui.config.WindowConfig.java

Source

/*
 * Copyright (c) 2008-2016 Haulmont.
 *
 * 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.haulmont.cuba.gui.config;

import com.haulmont.bali.util.Dom4j;
import com.haulmont.bali.util.Preconditions;
import com.haulmont.chile.core.model.MetaClass;
import com.haulmont.cuba.core.entity.Entity;
import com.haulmont.cuba.core.global.AppBeans;
import com.haulmont.cuba.core.global.DevelopmentException;
import com.haulmont.cuba.core.global.Metadata;
import com.haulmont.cuba.core.global.Resources;
import com.haulmont.cuba.core.sys.AppContext;
import com.haulmont.cuba.gui.NoSuchScreenException;
import com.haulmont.cuba.gui.components.Window;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.text.StrTokenizer;
import org.dom4j.Element;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationAwareOrderComparator;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;

import javax.annotation.Nullable;
import javax.inject.Inject;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * GenericUI class holding information about all registered in <code>screens.xml</code> screens.
 */
@Component(WindowConfig.NAME)
public class WindowConfig {

    public static final String NAME = "cuba_WindowConfig";

    public static final String WINDOW_CONFIG_XML_PROP = "cuba.windowConfig";

    public static final Pattern ENTITY_SCREEN_PATTERN = Pattern.compile("([_A-Za-z]+\\$[A-Z][_A-Za-z0-9]*)\\..+");

    private final Logger log = LoggerFactory.getLogger(WindowConfig.class);

    protected Map<String, List<WindowInfo>> screens = new HashMap<>();

    @Inject
    protected Resources resources;

    @Inject
    protected Metadata metadata;

    @Inject
    protected DeviceInfoProvider deviceInfoProvider;

    // Map alias -> ScreenAgent
    protected Map<String, ScreenAgent> activeScreenAgents;

    protected volatile boolean initialized;

    protected ReadWriteLock lock = new ReentrantReadWriteLock();

    protected void checkInitialized() {
        if (!initialized) {
            lock.readLock().unlock();
            lock.writeLock().lock();
            try {
                if (!initialized) {
                    init();
                    initialized = true;
                }
            } finally {
                lock.readLock().lock();
                lock.writeLock().unlock();
            }
        }
    }

    protected void init() {
        screens.clear();

        Map<String, ScreenAgent> agentMap = AppBeans.getAll(ScreenAgent.class);

        Map<String, ScreenAgent> screenAgents = new LinkedHashMap<>();
        List<ScreenAgent> availableAgents = new ArrayList<>(agentMap.values());

        AnnotationAwareOrderComparator.sort(availableAgents);

        for (ScreenAgent screenAgent : availableAgents) {
            screenAgents.put(screenAgent.getAlias(), screenAgent);
        }
        this.activeScreenAgents = screenAgents;

        String configName = AppContext.getProperty(WINDOW_CONFIG_XML_PROP);
        StrTokenizer tokenizer = new StrTokenizer(configName);
        for (String location : tokenizer.getTokenArray()) {
            Resource resource = resources.getResource(location);
            if (resource.exists()) {
                InputStream stream = null;
                try {
                    stream = resource.getInputStream();
                    loadConfig(Dom4j.readDocument(stream).getRootElement());
                } catch (IOException e) {
                    throw new RuntimeException("Unable to read window config from " + location, e);
                } finally {
                    IOUtils.closeQuietly(stream);
                }
            } else {
                log.warn("Resource {} not found, ignore it", location);
            }
        }
    }

    @SuppressWarnings("unchecked")
    protected void loadConfig(Element rootElem) {
        for (Element element : (List<Element>) rootElem.elements("include")) {
            String fileName = element.attributeValue("file");
            if (!StringUtils.isBlank(fileName)) {
                String incXml = resources.getResourceAsString(fileName);
                if (incXml == null) {
                    log.warn("File {} not found, ignore it", fileName);
                    continue;
                }
                loadConfig(Dom4j.readDocument(incXml).getRootElement());
            }
        }
        for (Element element : (List<Element>) rootElem.elements("screen")) {
            String id = element.attributeValue("id");
            if (StringUtils.isBlank(id)) {
                log.warn("Invalid window config: 'id' attribute not defined");
                continue;
            }

            ScreenAgent targetAgent = null;
            String agent = element.attributeValue("agent");
            if (StringUtils.isNotEmpty(agent)) {
                targetAgent = activeScreenAgents.get(agent);

                if (targetAgent == null) {
                    throw new DevelopmentException("Unable to find target screen agent", "agent", agent);
                }
            }

            WindowInfo windowInfo = new WindowInfo(id, element, targetAgent);

            List<WindowInfo> screenInfos = screens.get(id);
            if (screenInfos == null) {
                screenInfos = new ArrayList<>();
                screens.put(id, screenInfos);
            } else {
                WindowInfo existingScreen = screenInfos.stream().filter(
                        existingWindowInfo -> existingWindowInfo.getScreenAgent() == windowInfo.getScreenAgent())
                        .findFirst().orElse(null);

                if (existingScreen != null) {
                    screenInfos.remove(existingScreen);
                }
            }

            screenInfos.add(windowInfo);
        }
    }

    /**
     * Make the config to reload screens on next request.
     */
    public void reset() {
        initialized = false;
    }

    public WindowInfo findWindowInfo(String id) {
        return findWindowInfo(id, null);
    }

    /**
     * Get screen information by screen ID.
     *
     * @param id         screen ID as set up in <code>screens.xml</code>
     * @param deviceInfo target device info
     * @return screen's registration information or null if not found
     */
    @Nullable
    public WindowInfo findWindowInfo(String id, @Nullable DeviceInfo deviceInfo) {
        lock.readLock().lock();
        try {
            checkInitialized();

            List<WindowInfo> infos = screens.get(id);

            if (infos == null) {
                Matcher matcher = ENTITY_SCREEN_PATTERN.matcher(id);
                if (matcher.matches()) {
                    MetaClass metaClass = metadata.getClass(matcher.group(1));
                    if (metaClass == null) {
                        return null;
                    }

                    MetaClass originalMetaClass = metadata.getExtendedEntities().getOriginalMetaClass(metaClass);
                    if (originalMetaClass != null) {
                        String originalId = new StringBuilder(id)
                                .replace(matcher.start(1), matcher.end(1), originalMetaClass.getName()).toString();
                        infos = screens.get(originalId);
                    }
                }
            }

            List<WindowInfo> foundWindowInfos = infos;

            if (foundWindowInfos != null) {
                // do not perform stream processing in a simple case
                if (foundWindowInfos.size() == 1 && foundWindowInfos.get(0).getScreenAgent() == null) {
                    return foundWindowInfos.get(0);
                }

                if (deviceInfo == null) {
                    // find default screen
                    return foundWindowInfos.stream().filter(windowInfo -> windowInfo.getScreenAgent() == null)
                            .findFirst().orElse(null);
                } else {
                    return infos.stream().filter(
                            wi -> wi.getScreenAgent() != null && wi.getScreenAgent().isSupported(deviceInfo))
                            .findFirst()
                            .orElseGet(() -> foundWindowInfos.stream()
                                    .filter(windowInfo -> windowInfo.getScreenAgent() == null).findFirst()
                                    .orElse(null));
                }
            }

            return null;
        } finally {
            lock.readLock().unlock();
        }
    }

    /**
     * Get screen information by screen ID.
     *
     * @param id screen ID as set up in <code>screens.xml</code>
     * @return screen's registration information
     * @throws NoSuchScreenException if the screen with specified ID is not registered
     */
    public WindowInfo getWindowInfo(String id) {
        Preconditions.checkNotNullArgument(id, "Screen id cannot be null");

        return getWindowInfo(id, deviceInfoProvider.getDeviceInfo());
    }

    /**
     * Get screen information by screen ID.
     *
     * @param id         screen ID as set up in <code>screens.xml</code>
     * @param deviceInfo device info
     * @return screen's registration information
     * @throws NoSuchScreenException if the screen with specified ID is not registered
     */
    public WindowInfo getWindowInfo(String id, DeviceInfo deviceInfo) {
        WindowInfo windowInfo = findWindowInfo(id, deviceInfo);
        if (windowInfo == null) {
            throw new NoSuchScreenException(id);
        }
        return windowInfo;
    }

    /**
     * @return true if the configuration contains a screen with provided ID
     */
    public boolean hasWindow(String id) {
        return findWindowInfo(id) != null;
    }

    /**
     * All registered screens
     */
    public Collection<WindowInfo> getWindows() {
        lock.readLock().lock();
        try {
            checkInitialized();
            Collection<List<WindowInfo>> values = screens.values();
            return values.stream().flatMap(Collection::stream).collect(Collectors.toList());
        } finally {
            lock.readLock().unlock();
        }
    }

    public String getMetaClassScreenId(MetaClass metaClass, String suffix) {
        MetaClass screenMetaClass = metaClass;
        MetaClass originalMetaClass = metadata.getExtendedEntities().getOriginalMetaClass(metaClass);
        if (originalMetaClass != null) {
            screenMetaClass = originalMetaClass;
        }

        return screenMetaClass.getName() + suffix;
    }

    public String getBrowseScreenId(MetaClass metaClass) {
        return getMetaClassScreenId(metaClass, Window.BROWSE_WINDOW_SUFFIX);
    }

    public String getLookupScreenId(MetaClass metaClass) {
        return getMetaClassScreenId(metaClass, Window.LOOKUP_WINDOW_SUFFIX);
    }

    public String getEditorScreenId(MetaClass metaClass) {
        return getMetaClassScreenId(metaClass, Window.EDITOR_WINDOW_SUFFIX);
    }

    public WindowInfo getEditorScreen(Entity entity) {
        MetaClass metaClass = entity.getMetaClass();
        String editorScreenId = getEditorScreenId(metaClass);
        return getWindowInfo(editorScreenId);
    }

    /**
     * Get available lookup screen by class of entity
     *
     * @param entityClass entity class
     * @return id of lookup screen
     * @throws NoSuchScreenException if the screen with specified ID is not registered
     */
    public WindowInfo getLookupScreen(Class<? extends Entity> entityClass) {
        MetaClass metaClass = metadata.getSession().getClass(entityClass);
        String lookupScreenId = getAvailableLookupScreenId(metaClass);
        return getWindowInfo(lookupScreenId);
    }

    public String getAvailableLookupScreenId(MetaClass metaClass) {
        String id = getLookupScreenId(metaClass);
        if (!hasWindow(id)) {
            id = getBrowseScreenId(metaClass);
        }
        return id;
    }
}