| /* |
| * Copyright 2010 Google Inc. |
| * |
| * 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.google.gwt.editor.client.impl; |
| |
| import com.google.gwt.editor.client.CompositeEditor; |
| import com.google.gwt.editor.client.Editor; |
| import com.google.gwt.editor.client.EditorDelegate; |
| import com.google.gwt.editor.client.EditorError; |
| import com.google.gwt.editor.client.EditorVisitor; |
| import com.google.gwt.event.shared.HandlerRegistration; |
| |
| import java.util.ArrayList; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| /** |
| * A base implementation of EditorDelegate for use by generated types. |
| * |
| * @param <T> the type of object being edited |
| * @param <E> the type of editor |
| */ |
| public abstract class AbstractEditorDelegate<T, E extends Editor<T>> implements |
| EditorDelegate<T> { |
| |
| /** |
| * The machinery for attaching and detaching editors from the hierarchy via a |
| * {@link CompositeEditor}. An instance of a Chain is only created when |
| * necessary for a given hierarchy type. |
| * |
| * @param <R> the component element type |
| * @param <S> the component editor type |
| */ |
| protected class Chain<R, S extends Editor<R>> implements |
| CompositeEditor.EditorChain<R, S> { |
| private final CompositeEditor<T, R, S> composedEditor; |
| private final Class<R> composedElementType; |
| private final Map<S, AbstractEditorDelegate<R, S>> map = new LinkedHashMap<S, AbstractEditorDelegate<R, S>>(); |
| |
| /** |
| * Constructed via |
| * {@link AbstractEditorDelegate#createChain(CompositeEditor)}. |
| */ |
| Chain(CompositeEditor<T, R, S> composedEditor, Class<R> composedElementType) { |
| this.composedEditor = composedEditor; |
| this.composedElementType = composedElementType; |
| } |
| |
| public void accept(EditorVisitor visitor) { |
| for (AbstractEditorDelegate<R, S> delegate : map.values()) { |
| traverse(visitor, delegate); |
| } |
| } |
| |
| public void attach(R object, S subEditor) { |
| AbstractEditorDelegate<R, S> subDelegate = map.get(subEditor); |
| |
| String subPath = path + composedEditor.getPathElement(subEditor); |
| |
| if (subDelegate == null) { |
| @SuppressWarnings("unchecked") |
| AbstractEditorDelegate<R, S> temp = (AbstractEditorDelegate<R, S>) createComposedDelegate(); |
| subDelegate = temp; |
| map.put(subEditor, subDelegate); |
| addSubDelegate(subDelegate, subPath, subEditor); |
| } else { |
| subDelegate.path = subPath; |
| } |
| subDelegate.setObject(ensureMutable(object)); |
| traverse(new Initializer(), subDelegate); |
| } |
| |
| public void detach(S subEditor) { |
| map.remove(subEditor); |
| } |
| |
| public R getValue(S subEditor) { |
| AbstractEditorDelegate<R, S> subDelegate = map.get(subEditor); |
| if (subDelegate == null) { |
| return null; |
| } |
| return subDelegate.getObject(); |
| } |
| |
| void traverse(EditorVisitor visitor, AbstractEditorDelegate<R, S> delegate) { |
| R object = delegate.getObject(); |
| new RootEditorContext<R>(delegate, composedElementType, object).traverse( |
| visitor, delegate); |
| } |
| } |
| |
| protected static String appendPath(String prefix, String path) { |
| if ("".equals(prefix)) { |
| return path; |
| } else { |
| return prefix + "." + path; |
| } |
| } |
| |
| private boolean dirty; |
| private Chain<?, ?> editorChain; |
| private List<EditorError> errors; |
| private String path; |
| |
| public abstract void accept(EditorVisitor visitor); |
| |
| public abstract T getObject(); |
| |
| public String getPath() { |
| return path; |
| } |
| |
| /** |
| * Just returns the last value passed to {@link #setDirty(boolean)}. |
| */ |
| public boolean isDirty() { |
| return dirty; |
| } |
| |
| public void recordError(String message, Object value, Object userData) { |
| EditorError error = new SimpleError(this, message, value, userData); |
| errors.add(error); |
| } |
| |
| public void recordError(String message, Object value, Object userData, |
| String extraPath, Editor<?> leafEditor) { |
| EditorError error = new SimpleError(this, message, value, userData, |
| extraPath, leafEditor); |
| errors.add(error); |
| } |
| |
| public void setDirty(boolean dirty) { |
| this.dirty = dirty; |
| } |
| |
| public abstract HandlerRegistration subscribe(); |
| |
| /** |
| * Initialize a sub-delegate whenever one is added to the editor hierarchy. |
| */ |
| protected <R, S extends Editor<R>> void addSubDelegate( |
| AbstractEditorDelegate<R, S> subDelegate, String path, S subEditor) { |
| subDelegate.initialize(path, subEditor); |
| } |
| |
| protected String appendPath(String path) { |
| if (path.length() == 0) { |
| return this.path; |
| } |
| return appendPath(this.path, path); |
| } |
| |
| protected <R, S extends Editor<R>> void createChain( |
| Class<R> composedElementType) { |
| @SuppressWarnings("unchecked") |
| CompositeEditor<T, R, S> editor = (CompositeEditor<T, R, S>) getEditor(); |
| editorChain = new Chain<R, S>(editor, composedElementType); |
| } |
| |
| /** |
| * Only implemented by delegates for a {@link CompositeEditor}. |
| */ |
| protected AbstractEditorDelegate<?, ?> createComposedDelegate() { |
| throw new IllegalStateException(); |
| } |
| |
| protected <Q> Q ensureMutable(Q object) { |
| return object; |
| } |
| |
| protected abstract E getEditor(); |
| |
| protected Chain<?, ?> getEditorChain() { |
| return editorChain; |
| } |
| |
| protected List<EditorError> getErrors() { |
| return errors; |
| } |
| |
| protected void initialize(String pathSoFar, E editor) { |
| this.path = pathSoFar; |
| setEditor(editor); |
| errors = new ArrayList<EditorError>(); |
| initializeSubDelegates(); |
| } |
| |
| protected abstract void initializeSubDelegates(); |
| |
| protected abstract void setEditor(E editor); |
| |
| protected abstract void setObject(T object); |
| |
| /** |
| * Indicates whether or not calls to {@link #flush} are expected as part of |
| * normal operation. |
| */ |
| protected boolean shouldFlush() { |
| return true; |
| } |
| } |