blob: ed1e52be28619065bcf9c209373d7a9de1d74e1e [file] [log] [blame]
/*
* 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;
}
}