fr.putnami.pwt.core.model.client.ModelDriver.java Source code

Java tutorial

Introduction

Here is the source code for fr.putnami.pwt.core.model.client.ModelDriver.java

Source

/**
 * This file is part of pwt.
 *
 * pwt is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser
 * General Public License as published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * pwt is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along with pwt. If not,
 * see <http://www.gnu.org/licenses/>.
 */
package fr.putnami.pwt.core.model.client;

import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;

import java.util.Collection;
import java.util.Collections;
import java.util.List;

import fr.putnami.pwt.core.editor.client.Context;
import fr.putnami.pwt.core.editor.client.Driver;
import fr.putnami.pwt.core.editor.client.Editor;
import fr.putnami.pwt.core.editor.client.EditorComposite;
import fr.putnami.pwt.core.editor.client.EditorLeaf;
import fr.putnami.pwt.core.editor.client.Error;
import fr.putnami.pwt.core.editor.client.Path;
import fr.putnami.pwt.core.editor.client.Visitor;
import fr.putnami.pwt.core.editor.client.event.DataValidationEvent;
import fr.putnami.pwt.core.editor.client.event.DirtyEvent;
import fr.putnami.pwt.core.editor.client.event.FlushErrorEvent;
import fr.putnami.pwt.core.editor.client.event.FlushSuccessEvent;
import fr.putnami.pwt.core.editor.client.event.ResetDisplayEvent;
import fr.putnami.pwt.core.editor.client.helper.MessageHelper;
import fr.putnami.pwt.core.editor.client.impl.SimpleEditorContext;
import fr.putnami.pwt.core.editor.client.util.PathUtils;
import fr.putnami.pwt.core.model.client.exception.ModelNotSetException;
import fr.putnami.pwt.core.model.client.factory.ContextFactory;
import fr.putnami.pwt.core.model.client.model.Model;
import fr.putnami.pwt.core.model.client.visitor.BinderVisitor;
import fr.putnami.pwt.core.model.client.visitor.DirtyEventRegistrerVisitor;
import fr.putnami.pwt.core.model.client.visitor.DriverSetterVisitor;
import fr.putnami.pwt.core.model.client.visitor.EditorFactoryVisitor;
import fr.putnami.pwt.core.model.client.visitor.EnumValuesVisitor;
import fr.putnami.pwt.core.model.client.visitor.ErrorBinderVisitor;
import fr.putnami.pwt.core.model.client.visitor.FlusherVisitor;
import fr.putnami.pwt.core.model.client.visitor.HtmlForVisitor;
import fr.putnami.pwt.core.model.client.visitor.InputValidatorVisitor;
import fr.putnami.pwt.core.model.client.visitor.LabelFactoryVisitor;
import fr.putnami.pwt.core.model.client.visitor.LabelRendererVisitor;
import fr.putnami.pwt.core.model.client.visitor.MessageHelperContainerVisitor;
import fr.putnami.pwt.core.model.client.visitor.ModelInitializerVisitor;
import fr.putnami.pwt.core.model.client.visitor.PlaceholderRendererVisitor;
import fr.putnami.pwt.core.model.client.visitor.ReadonlyAspectVisitor;
import fr.putnami.pwt.core.model.client.visitor.TooltipRendererVisitor;

public class ModelDriver<T> implements Driver<T> {

    public static class DefaultContextFactory implements ContextFactory {
        @Override
        public <B extends Editor> Context<B> createContext(ModelDriver<?> driver, Context<?> parentContext,
                B editor) {
            Context<B> context = (Context<B>) driver.getContext(editor);
            if (context != null) {
                return context;
            }

            String path = editor.getPath();
            if (parentContext != null) {
                if (path == null || path.length() == 0) {
                    path = parentContext.getPath().toString();
                } else if (parentContext.getPath().toString().length() > 0) {
                    if (path.startsWith("[") && path.endsWith("]")) {
                        path = parentContext.getPath().toString() + path;
                    } else {
                        path = parentContext.getPath().toString() + Path.SEPARATOR_PATH + path;
                    }
                }
            }
            if (driver.getRootEditor() == editor) {
                path = null;
            }

            context = new SimpleEditorContext<B>(driver, parentContext, editor, PathUtils.evalPath(path));
            driver.addContext(context);

            driver.accept(new ModelInitializerVisitor(), context);
            driver.accept(new DriverSetterVisitor(), context);
            driver.accept(new LabelFactoryVisitor(), context);
            driver.accept(new EditorFactoryVisitor(), context);
            driver.accept(new LabelRendererVisitor(), context);
            driver.accept(new PlaceholderRendererVisitor(), context);
            driver.accept(new MessageHelperContainerVisitor(), context);
            driver.accept(new TooltipRendererVisitor(), context);
            driver.accept(new InputValidatorVisitor(), context);
            driver.accept(driver.htmlForVisitor, context);
            driver.accept(new DirtyEventRegistrerVisitor(driver.dirtyHandler), context);
            driver.accept(new EnumValuesVisitor(), context);
            driver.accept(new ReadonlyAspectVisitor(), context);

            for (Visitor visitor : Iterables.filter(driver.visitors, Visitor.VisitorTrigger.INITALIZE)) {
                driver.accept(visitor, context);
            }

            if (editor instanceof EditorComposite && (parentContext == null || !(editor instanceof EditorLeaf))) {
                for (Editor child : ((EditorComposite) editor).getEditors()) {
                    this.createContext(driver, context, child);
                }
            }

            return context;
        }
    }

    private final HtmlForVisitor htmlForVisitor = new HtmlForVisitor();

    private final Model<T> model;
    private final List<Context<?>> contexts = Lists.newArrayList();
    private final List<Visitor> visitors = Lists.newArrayList();

    private final List<Error> errors = Lists.newArrayList();

    boolean autoFlush = false;
    private final DirtyEvent.Handler dirtyHandler = new DirtyEvent.Handler() {

        @Override
        public void onDirtyEvent(DirtyEvent event) {
            ModelDriver.this.dirty = true;
            if (ModelDriver.this.autoFlush) {
                ModelDriver.this.flush();
            } else if (event.getEditor() != ModelDriver.this.rootEditor) {
                DirtyEvent.fire(ModelDriver.this.rootEditor);
            }
        }
    };

    private MessageHelper messageHelper;
    private Editor rootEditor;
    private Context<Editor> rootContext;

    private boolean dirty;

    private T value;
    private T displayedValue;

    public ModelDriver(Model<T> model) {
        this.model = model;
        if (model == null) {
            throw new ModelNotSetException();
        }
    }

    public Model<T> getModel() {
        return this.model;
    }

    public T getValue() {
        return this.value;
    }

    public T getDisplayedValue() {
        return this.displayedValue;
    }

    public void setDisplayedValue(T displayedValue) {
        this.displayedValue = displayedValue;
    }

    public void setValue(T value) {
        this.value = value;
    }

    public boolean isAutoFlush() {
        return this.autoFlush;
    }

    public void setAutoFlush(boolean autoFlush) {
        this.autoFlush = autoFlush;
    }

    public Context<Editor> getRootContext() {
        return this.rootContext;
    }

    @Override
    public <E extends Editor> E getRootEditor() {
        return (E) this.rootEditor;
    }

    @Override
    public Iterable<Error> getErrors() {
        return this.errors == null ? Collections.<Error>emptyList() : Iterables.unmodifiableIterable(this.errors);
    }

    @Override
    public boolean hasErrors() {
        return this.errors != null && !this.errors.isEmpty();
    }

    @Override
    public boolean isDirty() {
        return this.dirty;
    }

    @Override
    public Collection<Visitor> getVisitors() {
        return Collections.unmodifiableList(this.visitors);
    }

    public Context<?> getContext(final Editor editor) {
        return Iterables.find(this.contexts, new Predicate<Context<?>>() {
            @Override
            public boolean apply(Context<?> context) {
                return context.getEditor() == editor;
            }
        }, null);
    }

    public boolean removeContext(final Context<?> context) {
        if (this.contexts.remove(context)) {
            List<Context<?>> children = Lists.newArrayList();
            for (Context<?> child : this.contexts) {
                if (context.equals(child.getParentContext())) {
                    children.add(context);
                }
            }
            for (Context<?> childContext : children) {
                this.removeContext(childContext);
            }
            return true;
        }
        return false;
    }

    public boolean addContext(Context<?> context) {
        if (context != null && !this.contexts.contains(context)) {
            this.contexts.add(context);
            return true;
        }
        return false;
    }

    @Override
    public MessageHelper getMessageHelper() {
        return this.messageHelper;
    }

    public void setMessageHelper(MessageHelper messageHelper) {
        this.messageHelper = messageHelper;
    }

    @Override
    public void initialize(Editor editor, Visitor... visitors) {
        assert this.rootContext == null : "already initialized";
        this.rootEditor = editor;
        if (visitors != null) {
            Iterables.addAll(this.visitors, Lists.newArrayList(visitors));
            Collections.sort(this.visitors, new Visitor.VisitorComparator());
        }
        this.rootContext = ContextFactory.Util.get().createContext(this, null, editor);
    }

    @Override
    public boolean registerVisitor(Visitor visitor) {
        if (visitor == null) {
            return false;
        }
        this.visitors.add(visitor);
        Collections.sort(this.visitors, new Visitor.VisitorComparator());
        if (Visitor.VisitorTrigger.INITALIZE == visitor.trigerOn()) {
            return this.accept(visitor);
        }
        return true;
    }

    @Override
    public void edit(T value) {
        this.value = value;
        this.resetDisplay();
    }

    @Override
    public void resetDisplay() {
        this.errors.clear();
        this.accept(new ErrorBinderVisitor(this.model, this.messageHelper, this.errors));

        this.displayedValue = this.value;

        for (Visitor visitor : Iterables.filter(this.visitors, Visitor.VisitorTrigger.BEFORE_EDIT)) {
            this.accept(visitor);
        }

        this.accept(new BinderVisitor(this, this.displayedValue));

        for (Visitor visitor : Iterables.filter(this.visitors, Visitor.VisitorTrigger.AFTER_EDIT)) {
            this.accept(visitor);
        }
        ResetDisplayEvent.fire(this.rootEditor, this.displayedValue);
    }

    @Override
    public T flush() {
        this.errors.clear();

        T result = this.model.cloneBean(this.value);
        FlusherVisitor<T> flusher = new FlusherVisitor<T>(this.model, result);
        result = flusher.getTargetValue();
        this.accept(flusher);
        result = flusher.getTargetValue();
        if (flusher.hasErrors()) {
            Iterables.addAll(this.errors, flusher.getErrors());
        } else {
            DataValidationEvent validationEvent = DataValidationEvent.fire(this.rootEditor, result);
            this.errors.addAll(validationEvent.getErrors());
        }
        if (!this.errors.isEmpty()) {
            this.accept(new ErrorBinderVisitor(this.model, this.messageHelper, this.errors));
            FlushErrorEvent.fire(this.rootEditor, this.value, result, this.errors);
            result = this.value;
        } else {
            this.value = result;
            FlushSuccessEvent.fire(this.rootEditor, result);
        }

        for (Visitor visitor : Iterables.filter(this.visitors, Visitor.VisitorTrigger.FLUSH)) {
            this.accept(visitor);
        }

        return result;
    }

    @Override
    public boolean accept(Visitor visitor) {
        if (!visitor.beforeVisit()) {
            return false;
        }
        for (int i = 0; i < this.contexts.size(); i++) {
            Context<?> context = this.contexts.get(i);
            if (context != null && !visitor.visit(context)) {
                break;
            }
        }
        if (!visitor.afterVisit()) {
            return false;
        }
        return true;
    }

    public boolean accept(Visitor visitor, Context<?> context) {
        if (!visitor.beforeVisit()) {
            return false;
        }
        if (context == null || !visitor.visit(context)) {
            return false;
        }
        if (!visitor.afterVisit()) {
            return false;
        }
        return true;
    }
}