| /* |
| * Copyright 2008 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.core.ext.soyc.impl; |
| |
| import com.google.gwt.core.ext.soyc.Member; |
| import com.google.gwt.dev.jjs.ast.JDeclaredType; |
| import com.google.gwt.dev.jjs.ast.JField; |
| import com.google.gwt.dev.jjs.ast.JMethod; |
| |
| import java.lang.reflect.Constructor; |
| import java.lang.reflect.InvocationTargetException; |
| import java.util.IdentityHashMap; |
| import java.util.Map; |
| |
| /** |
| * A factory object for the standard implementations of Member subtypes. The |
| * factory methods in this type provide canonicalized instances. The maps used |
| * by MemberFactory use hard, identity-based references. |
| */ |
| public class MemberFactory { |
| private final Map<Class<?>, Map<?, ?>> map = new IdentityHashMap<Class<?>, Map<?, ?>>(); |
| |
| public StandardClassMember get(JDeclaredType type) { |
| return getOrCreate(type, StandardClassMember.class, JDeclaredType.class); |
| } |
| |
| public StandardFieldMember get(JField field) { |
| return getOrCreate(field, StandardFieldMember.class, JField.class); |
| } |
| |
| public StandardMethodMember get(JMethod method) { |
| return getOrCreate(method, StandardMethodMember.class, JMethod.class); |
| } |
| |
| @SuppressWarnings("unchecked") |
| private <K, V extends Member> Map<K, V> getElementMap(Class<V> clazz) { |
| Map<K, V> elementMap = (Map<K, V>) map.get(clazz); |
| if (elementMap == null) { |
| elementMap = new IdentityHashMap<K, V>(); |
| map.put(clazz, elementMap); |
| } |
| return elementMap; |
| } |
| |
| /** |
| * Assumes that the implementation of Member has a two-arg constructor that |
| * accepts a MemberFactory and the key. |
| * |
| * @param <K> the type of key used to canonicalize the mapping |
| * @param <V> the type of Member implementation to use |
| * @param key the key by which the value should be canonicalized |
| * @param implClazz the concrete type of Member to construct |
| * @param constructorParam the declared type of the second parameter of the |
| * concrete Member type |
| * @return the canonicalized instance of Member for the given key |
| */ |
| private <K, V extends Member> V getOrCreate(K key, Class<V> implClazz, |
| Class<? super K> constructorParam) { |
| Map<K, V> elementMap = getElementMap(implClazz); |
| |
| V toReturn = elementMap.get(key); |
| if (toReturn == null) { |
| try { |
| Constructor<V> ctor = implClazz.getConstructor(MemberFactory.class, constructorParam); |
| toReturn = ctor.newInstance(this, key); |
| } catch (NoSuchMethodException e) { |
| throw new RuntimeException(implClazz.getName() + " must declare a two-arg (MemberFactory, " |
| + constructorParam.getName() + ") constructor", e); |
| } catch (IllegalArgumentException e) { |
| // Error on the part of this type |
| throw new RuntimeException(e); |
| } catch (InstantiationException e) { |
| // Error on the part of this type, asking for a non-instantiable type |
| throw new RuntimeException(e); |
| } catch (IllegalAccessException e) { |
| // Error on the part of the coder of implClazz |
| throw new RuntimeException(e); |
| } catch (InvocationTargetException e) { |
| // Probably a RuntimeException thrown from the constructor |
| if (e.getCause() instanceof RuntimeException) { |
| throw (RuntimeException) e.getCause(); |
| } |
| throw new RuntimeException(e); |
| } |
| |
| elementMap.put(key, toReturn); |
| } |
| |
| return toReturn; |
| } |
| } |