/* * 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.dev.javac.typemodel; import com.google.gwt.core.ext.typeinfo.JType; import com.google.gwt.dev.util.collect.HashMap; import com.google.gwt.dev.util.collect.Lists; import com.google.gwt.dev.util.collect.Maps; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * Class that initializes the different members of a * {@link JDelegatingClassType} from its corresponding base type on demand. */ class DelegateMembers extends AbstractMembers { /** * Implementation note: it is critical that everything in this class is * computed as lazily as possible. Many, many more parameterized types, raw * types, type bindings, or wilcard types can be created than real classes, * and computing anything up front would add runtime overhead and memory. */ private final JClassType baseType; private Map<String, JField> fieldMap; private JField[] fields; private List<JConstructor> lazyConstructors; private Map<String, Object> methodMap; private JMethod[] methods; private final Substitution substitution; /** */ public DelegateMembers(JDelegatingClassType enclosingType, JClassType baseType, Substitution substitution) { super(enclosingType); this.baseType = baseType; this.substitution = substitution; } @Override public JField findField(String name) { initFields(); return fieldMap.get(name); } @Override public JField[] getFields() { initFields(); return fields.length == 0 ? fields : fields.clone(); } @Override public JMethod[] getMethods() { initMethods(); return methods.length == 0 ? methods : methods.clone(); } @Override public JMethod[] getOverloads(String name) { initMethods(); Object object = methodMap.get(name); if (object == null) { return TypeOracle.NO_JMETHODS; } else if (object instanceof JMethod) { return new JMethod[]{(JMethod) object}; } else { return ((JMethod[]) object).clone(); } } @Override protected void addConstructor(JConstructor ctor) { throw new UnsupportedOperationException(); } @Override protected void addField(JField field) { throw new UnsupportedOperationException(); } @Override protected void addMethod(JMethod method) { throw new UnsupportedOperationException(); } @Override protected List<JConstructor> doGetConstructors() { if (lazyConstructors != null) { /* * Return if the constructors are being initialized or have been * initialized. */ return lazyConstructors; } lazyConstructors = new ArrayList<JConstructor>(); JConstructor[] baseCtors = baseType.getConstructors(); for (JConstructor baseCtor : baseCtors) { JConstructor newCtor = new JConstructor(getParentType(), baseCtor); initializeParams(baseCtor, newCtor); lazyConstructors.add(newCtor); } return lazyConstructors = Lists.normalize(lazyConstructors); } @Override protected Map<String, JClassType> doGetNestedTypes() { // TODO: is this correct? return Maps.create(); } private void initFields() { if (fields != null) { return; } // Transitively sorted. fields = baseType.getFields(); fieldMap = new HashMap<String, JField>(); for (int i = 0; i < fields.length; ++i) { JField baseField = fields[i]; JField newField = new JField(getParentType(), baseField); newField.setType(substitute(baseField.getType())); fields[i] = newField; fieldMap.put(newField.getName(), newField); } fieldMap = Maps.normalize(fieldMap); } private void initializeExceptions(JAbstractMethod srcMethod, JAbstractMethod newMethod) { for (JClassType thrown : srcMethod.getThrows()) { // exceptions cannot be parameterized; just copy them over newMethod.addThrows(thrown); } } private void initializeParams(JAbstractMethod srcMethod, JAbstractMethod newMethod) { for (JParameter srcParam : srcMethod.getParameters()) { JParameter newParam = new JParameter(newMethod, srcParam); newParam.setType(substitute(srcParam.getType())); newMethod.addParameter(newParam); } } @SuppressWarnings("unchecked") private void initMethods() { if (methods != null) { return; } // Transitively sorted. methods = baseType.getMethods(); methodMap = new HashMap<String, Object>(); for (int i = 0; i < methods.length; ++i) { JMethod baseMethod = methods[i]; JMethod newMethod = new JMethod(getParentType(), baseMethod); initializeParams(baseMethod, newMethod); newMethod.setReturnType(substitute(baseMethod.getReturnType())); initializeExceptions(baseMethod, newMethod); methods[i] = newMethod; String methodName = newMethod.getName(); Object object = methodMap.get(methodName); if (object == null) { methodMap.put(methodName, newMethod); } else if (object instanceof JMethod) { List<JMethod> list = new ArrayList<JMethod>(2); list.add((JMethod) object); list.add(newMethod); methodMap.put(methodName, list); } else { List<JMethod> list = (List<JMethod>) object; list.add(newMethod); } } // Replace the ArrayLists with plain arrays. for (String methodName : methodMap.keySet()) { Object object = methodMap.get(methodName); if (object instanceof List) { List<JMethod> list = (List<JMethod>) object; methodMap.put(methodName, list.toArray(TypeOracle.NO_JMETHODS)); } } methodMap = Maps.normalize(methodMap); } private JType substitute(JType type) { if (type instanceof JClassType) { return substitution.getSubstitution((JClassType) type); } return type; } }