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

org.wings.DefaultReloadManager Maven / Gradle / Ivy

The newest version!
package org.wings;

import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.wings.plaf.Update;

/**
 * Default implementation of the reload manager.
 *
 * @author Stephan Schuster
 */
public class DefaultReloadManager implements ReloadManager {

    private static final long serialVersionUID = 2951486214657704539L;

    private final transient static Logger log = LoggerFactory.getLogger(DefaultReloadManager.class);

    private int updateCount = 0;

    private boolean updateMode = false;
    
    private boolean suppressMode = false;

    private boolean acceptChanges = true;

    private final Map fullReplaceUpdates = new HashMap<>(256);

    private final Map> fineGrainedUpdates = new HashMap<>(64);

    /** 
     * All the components to reload. A LinkedHashSet to have an ordered sequence that allows for fast lookup.
     */
    private final Set componentsToReload = new LinkedHashSet<>();
    
    @Override
    public void reload(SComponent component) {
        if (suppressMode) {
            return;
        }
        
        if (component == null)
            throw new IllegalArgumentException("Component must not be null!");        

        if (updateMode) {
            addUpdate(component, null);
        } else {
            if (!componentsToReload.contains(component)) {
                componentsToReload.add(component);
            }
        }
    }

    @Override
    @SuppressWarnings("unchecked")
	public void addUpdate(SComponent component, Update update) {
        if (component == null)
            throw new IllegalArgumentException("Component must not be null!");

        if (update == null) {
            update = component.getCG().getComponentUpdate(component);
            if (update == null) {
                SFrame frame = component.getParentFrame();
                if (frame != null)
                    fullReplaceUpdates.put(frame, null);
                return;
            }
        }

        component = update.getComponent();

        if (acceptChanges) {
            PotentialUpdate potentialUpdate = new PotentialUpdate(update);

            if ((update.getProperty() & Update.FULL_REPLACE_UPDATE) == Update.FULL_REPLACE_UPDATE) {
                fullReplaceUpdates.put(component, potentialUpdate);
            } else {
                Set potentialUpdates = getFineGrainedUpdates(component);
                potentialUpdates.remove(potentialUpdate);
                potentialUpdates.add(potentialUpdate);
                fineGrainedUpdates.put(component, potentialUpdates);
            }
        } else if (log.isDebugEnabled()) {
            //log.debug("Component " + component + " changed after invalidation of frames.");
        }
    }

    @Override
    @SuppressWarnings({"unchecked"})
    public List getUpdates() {
        if (!componentsToReload.isEmpty()) {
            for (SComponent aComponentsToReload : componentsToReload) {
                boolean tmp = acceptChanges;
                acceptChanges = true;
                addUpdate(aComponentsToReload, null);
                acceptChanges = tmp;
            }
        }

        filterUpdates();

        List filteredUpdates = fullReplaceUpdates.values().stream().filter(potentialUpdate -> potentialUpdate != null).collect(Collectors.toList());
        for (Set updates : fineGrainedUpdates.values()) {
            filteredUpdates.addAll(updates.stream().filter(potentialUpdate -> potentialUpdate != null).collect(Collectors.toList()));
        }
        Collections.sort(filteredUpdates, getUpdateComparator());

        return (List) filteredUpdates;
    }

    @Override
    public Set getDirtyComponents() {
        final Set dirtyComponents = new HashSet<>();
        dirtyComponents.addAll(fullReplaceUpdates.keySet());
        dirtyComponents.addAll(fineGrainedUpdates.keySet());
        dirtyComponents.addAll(componentsToReload);
        return dirtyComponents;
    }

    @Override
    public Set getDirtyFrames() {
        final Set dirtyFrames = new HashSet<>(5);
        for (Object o : getDirtyComponents()) {
            SFrame parentFrame = ((SComponent) o).getParentFrame();
            if (parentFrame != null) {
                dirtyFrames.add(parentFrame);
            }
        }
        return dirtyFrames;
    }

    @Override
    public void invalidateFrames() {
        Iterator i = getDirtyFrames().iterator();
        while (i.hasNext()) {
            ((SFrame) i.next()).invalidate();
            i.remove();
        }
        acceptChanges = false;
    }

    @Override
    public void clear() {
        updateCount = 0;
        acceptChanges = true;
        fullReplaceUpdates.clear();
        fineGrainedUpdates.clear();
        componentsToReload.clear();
    }

    @Override
    public boolean isUpdateMode() {
        return updateMode;
    }

    @Override
    public void setUpdateMode(boolean updateMode) {
        this.updateMode = updateMode;
    }

    @Override
    public boolean isSuppressMode() {
        return suppressMode;
    }

    @Override
    public void setSuppressMode(boolean suppressMode) {
        this.suppressMode = suppressMode;
    }

    @Override
    public boolean isReloadRequired(SFrame frame) {
        if (updateMode)
            return fullReplaceUpdates.containsKey(frame);
        else
            return true;
    }

    protected Set getFineGrainedUpdates(SComponent component) {
        Set potentialUpdates = fineGrainedUpdates.get(component);
        if (potentialUpdates == null) {
            potentialUpdates = new HashSet<>(5);
        }
        return potentialUpdates;
    }

    protected void filterUpdates() {
        if (log.isDebugEnabled())
            printAllUpdates("Potential updates:");

        fineGrainedUpdates.keySet().removeAll(fullReplaceUpdates.keySet());

        SortedMap componentHierarchy = new TreeMap<>();

        for (SComponent component : getDirtyComponents()) {
            if (component.getParentFrame() == null || !component.isVisible() || (!component.isRecursivelyVisible() && !(component instanceof SMenu))) {
                fullReplaceUpdates.remove(component);
                fineGrainedUpdates.remove(component);
            } else {
                componentHierarchy.put(getPath(component), component);
            }
        }
        
        for (Iterator i = componentHierarchy.keySet().iterator(); i.hasNext();) {
            final String topPath = (String) i.next();
            final String comparePath = (topPath + '/').substring(1); // get rid of depth
            if (fullReplaceUpdates.containsKey(componentHierarchy.get(topPath))) {
                while (i.hasNext()) {
                    final String subPath = (String) i.next();
                    if (subPath.substring(1).startsWith(comparePath)) {
                        fullReplaceUpdates.remove(componentHierarchy.get(subPath));
                        fineGrainedUpdates.remove(componentHierarchy.get(subPath));
                        i.remove();
                    }
                }
            }
            i = componentHierarchy.tailMap(topPath + '\0').keySet().iterator();
        }

        if (log.isDebugEnabled())
            printAllUpdates("Effective updates:");
    }
    
    /**
     * Return the path of the component. The first character denotes the depth of the path.
     */
    private static String getPath(SComponent component) {
        return getPath(new StringBuilder("0"), component).toString();
    }

    private static StringBuilder getPath(StringBuilder builder, SComponent component) {
        if (component == null)
            return builder;
        if (component.getClientProperty("drm:realParentComponent") != null) {
            Object parent = component.getClientProperty("drm:realParentComponent");
            if (!(parent instanceof SComponent)) parent = null;
            return getPath(builder, (SComponent) parent).append('/').append(component.getName());
        } else {
            builder.setCharAt(0, (char) (builder.charAt(0) + 1)); // increase depth
            return getPath(builder, component.getParent()).append('/').append(component.getName());
        }
    }

    private void printAllUpdates(String header) {
        log.debug(header);
        int numberOfUpdates = 0;
        StringBuilder output = new StringBuilder(512);
        for (SComponent component : getDirtyComponents()) {
            output.setLength(0);
            output.append("    ").append(component).append(":");
            if (fullReplaceUpdates.containsKey(component)) {
                output.append(" ").append(fullReplaceUpdates.get(component));
                if (fullReplaceUpdates.get(component) == null) {
                    output.append(" [no component update supported --> reload frame!!!]");
                }
                ++numberOfUpdates;
            }
            for (PotentialUpdate potentialUpdate : getFineGrainedUpdates(component)) {
                output.append(" ").append(potentialUpdate);
                ++numberOfUpdates;
            }
            log.debug(output.toString());
        }
        log.debug("    --> " + numberOfUpdates + " updates");
    }

    private final class PotentialUpdate implements Update {

        private Update update;
        private int position;

        public PotentialUpdate(Update update) {
            this.update = update;
            this.position = updateCount++;
        }

        @Override
        public SComponent getComponent() {
            return update.getComponent();
        }

        @Override
        public Handler getHandler() {
            return update.getHandler();
        }

        @Override
        public int getProperty() {
            return update.getProperty();
        }

        @Override
        public int getPriority() {
            return update.getPriority();
        }

        public int getPosition() {
            return position;
        }

        @Override
        public boolean equals(Object object) {
            if (object == this)
                return true;
            if (object == null || object.getClass() != this.getClass())
                return false;

            PotentialUpdate other = (PotentialUpdate) object;

            return update.equals(other.update);
        }

        @Override
        public int hashCode() {
            return update.hashCode();
        }

        @Override
        public String toString() {
            String clazz = update.getClass().getName();
            int index = clazz.lastIndexOf('$');
            if (index < 0)
                index = clazz.lastIndexOf('.');
            return clazz.substring(++index) + '[' + getPriority() + '|' + position + ']';
        }

    }

    private Comparator getUpdateComparator() {
        return
                new CombinedComparator<>(
                        new InverseComparator<>(new PriorityComparator()),
                        new PositionComparator()
                );
    }

    private static class PositionComparator implements Comparator {

        @Override
        public int compare(PotentialUpdate object1, PotentialUpdate object2) {
            if (object1.getPosition() < object2.getPosition()) return -1;
            if (object1.getPosition() > object2.getPosition()) return 1;
            return 0;
        }

    }

    private static class PriorityComparator implements Comparator {

        @Override
        public int compare(PotentialUpdate object1, PotentialUpdate object2) {
            if (object1.getPriority() < object2.getPriority()) return -1;
            if (object1.getPriority() > object2.getPriority()) return 1;
            return 0;
        }

    }

    private static class CombinedComparator implements Comparator {

        private Comparator comparator1;
        private Comparator comparator2;

        public CombinedComparator(Comparator c1, Comparator c2) {
            this.comparator1 = c1;
            this.comparator2 = c2;
        }

        @Override
        public int compare(T object1, T object2) {
            int result = comparator1.compare(object1, object2);
            if (result == 0)
                return comparator2.compare(object1, object2);
            else
                return result;
        }
    }

    private static class InverseComparator implements Comparator {

        private Comparator comparator;

        public InverseComparator(Comparator c) {
            this.comparator = c;
        }

        @Override
        public int compare(T object1, T object2) {
            return -comparator.compare(object1, object2);
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy