| /* |
| * 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.core.client.impl.WeakMapping; |
| import com.google.gwt.editor.client.AutoBean; |
| import com.google.gwt.editor.client.AutoBeanUtils; |
| import com.google.gwt.editor.client.AutoBeanVisitor; |
| import com.google.gwt.editor.client.AutoBeanVisitor.Context; |
| |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| |
| /** |
| * Basic implementation. |
| * |
| * @param <T> the wrapper type |
| */ |
| public abstract class AbstractAutoBean<T> implements AutoBean<T> { |
| /** |
| * Used to avoid cycles when visiting. |
| */ |
| public static class OneShotContext implements Context { |
| private final Set<AbstractAutoBean<?>> seen = new HashSet<AbstractAutoBean<?>>(); |
| } |
| |
| protected static final Object[] EMPTY_OBJECT = new Object[0]; |
| |
| /** |
| * Used by {@link #createSimplePeer()}. |
| */ |
| protected final Map<String, Object> values; |
| |
| private boolean frozen; |
| /** |
| * Lazily initialized by {@link #setTag(String, Object)} because not all |
| * instances will make use of tags. |
| */ |
| private Map<String, Object> tags; |
| private final boolean usingSimplePeer; |
| private T wrapped; |
| |
| /** |
| * Constructor that will use a generated simple peer. |
| */ |
| protected AbstractAutoBean() { |
| usingSimplePeer = true; |
| values = new HashMap<String, Object>(); |
| wrapped = createSimplePeer(); |
| |
| // Used by AutoBeanUtils |
| WeakMapping.set(wrapped, AutoBean.class.getName(), this); |
| } |
| |
| /** |
| * Clone constructor. |
| */ |
| protected AbstractAutoBean(AbstractAutoBean<T> toClone, boolean deep) { |
| if (!toClone.usingSimplePeer) { |
| throw new IllegalStateException("Cannot clone wrapped bean"); |
| } |
| if (toClone.tags != null) { |
| tags = new HashMap<String, Object>(toClone.tags); |
| } |
| usingSimplePeer = true; |
| values = new HashMap<String, Object>(toClone.values); |
| wrapped = createSimplePeer(); |
| |
| if (deep) { |
| for (Map.Entry<String, Object> entry : values.entrySet()) { |
| AutoBean<?> auto = AutoBeanUtils.getAutoBean(entry.getValue()); |
| if (auto != null) { |
| entry.setValue(auto.clone(true).as()); |
| } |
| } |
| } |
| |
| // Used by AutoBeanUtils |
| WeakMapping.set(wrapped, AutoBean.class.getName(), this); |
| } |
| |
| /** |
| * Constructor that wraps an existing object. |
| */ |
| protected AbstractAutoBean(T wrapped) { |
| usingSimplePeer = false; |
| values = null; |
| this.wrapped = wrapped; |
| |
| // Used by AutoBeanUtils |
| WeakMapping.set(wrapped, AutoBean.class.getName(), this); |
| } |
| |
| public void accept(AutoBeanVisitor visitor) { |
| traverse(visitor, new OneShotContext()); |
| } |
| |
| public abstract T as(); |
| |
| public abstract AutoBean<T> clone(boolean deep); |
| |
| @SuppressWarnings("unchecked") |
| public <Q> Q getTag(String tagName) { |
| return tags == null ? null : (Q) tags.get(tagName); |
| } |
| |
| public boolean isFrozen() { |
| return frozen; |
| } |
| |
| public boolean isWrapper() { |
| return !usingSimplePeer; |
| } |
| |
| public void setFrozen(boolean frozen) { |
| this.frozen = frozen; |
| } |
| |
| public void setTag(String tagName, Object value) { |
| if (tags == null) { |
| tags = new HashMap<String, Object>(); |
| } |
| tags.put(tagName, value); |
| } |
| |
| public void traverse(AutoBeanVisitor visitor, OneShotContext ctx) { |
| // Avoid cycles |
| if (!ctx.seen.add(this)) { |
| return; |
| } |
| if (visitor.visit(this, ctx)) { |
| traverseProperties(visitor, ctx); |
| } |
| visitor.endVisit(this, ctx); |
| } |
| |
| public T unwrap() { |
| if (usingSimplePeer) { |
| throw new IllegalStateException(); |
| } |
| try { |
| WeakMapping.set(wrapped, AutoBean.class.getName(), null); |
| return wrapped; |
| } finally { |
| wrapped = null; |
| } |
| } |
| |
| /** |
| * No-op. Used as a debugger hook point for generated code. |
| */ |
| protected void call(String method, Object returned, Object... parameters) { |
| } |
| |
| protected void checkFrozen() { |
| if (frozen) { |
| throw new IllegalStateException("The AutoBean has been frozen"); |
| } |
| } |
| |
| protected void checkWrapped() { |
| if (wrapped == null) { |
| throw new IllegalStateException("The AutoBean has been unwrapped"); |
| } |
| } |
| |
| protected T createSimplePeer() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * No-op. Used as a debugger hook point for generated code. |
| */ |
| protected <V> V get(String method, V toReturn) { |
| return toReturn; |
| } |
| |
| protected <W> W getFromWrapper(W obj) { |
| // Some versions of javac have problem inferring the generics here |
| return AutoBeanUtils.<W, W> getAutoBean(obj).as(); |
| } |
| |
| protected T getWrapped() { |
| return wrapped; |
| } |
| |
| protected boolean isWrapped(Object obj) { |
| return AutoBeanUtils.getAutoBean(obj) != null; |
| } |
| |
| /** |
| * No-op. Used as a debugger hook point for generated code. |
| */ |
| protected void set(String method, Object value) { |
| } |
| |
| protected abstract void traverseProperties(AutoBeanVisitor visitor, |
| OneShotContext ctx); |
| } |