All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.nullschool.grains.generate.SymbolTable Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2013 Cameron Beccario
 *
 * 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 net.nullschool.grains.generate;

import net.nullschool.collect.basic.BasicCollections;
import net.nullschool.grains.*;
import net.nullschool.grains.GrainProperty.Flag;
import net.nullschool.reflect.LateParameterizedType;

import java.beans.*;
import java.lang.reflect.*;
import java.util.*;

import static net.nullschool.grains.generate.GenerateTools.*;
import static net.nullschool.collect.basic.BasicCollections.*;
import static net.nullschool.reflect.TypeTools.*;


/**
 * 2013-05-09

* * Organizes and constructs the {@link Symbol} instances used for code generation. * * @author Cameron Beccario */ final class SymbolTable { private final Class schema; private final TypeTable typeTable; private final TypePrinterFactory printerFactory; private final Member typePolicyMember; SymbolTable(Class schema, TypeTable typeTable, TypePrinterFactory printerFactory, Member typePolicyMember) { this.schema = schema; this.typeTable = typeTable; this.printerFactory = printerFactory; this.typePolicyMember = typePolicyMember; } private static Type cook(Type type) { return new Cook().apply(type); } private Type immutify(Type type) { return new Immutify(typeTable).apply(type); } private static Type dewildcard(Type type) { return new DeWildcard().apply(type); } private static Set flagsFor(PropertyDescriptor pd) { return pd.getReadMethod().getName().startsWith("is") ? setOf(Flag.IS_PROPERTY) : BasicCollections.emptySet(); } /** * Returns true if the left type is wider than the right type, i.e., the right type is more specific than the * left type. */ private static boolean isWider(Type left, Type right) { // UNDONE: use proper widening conversion check that follows rules in JLS7 §4.10 and §5.1.5. return erase(left).isAssignableFrom(erase(right)); } /** * Collects all grain properties explicitly defined on the specified type. This method uses the JavaBean * introspector to identify the properties. Type variables are replaced with their appropriate type arguments, * if any. * * @param type the type to introspect. * @return a list of declared grain properties. * @throws IntrospectionException if an exception occurs during introspection. */ static List collectDeclaredProperties(Type type) throws IntrospectionException { List results = new ArrayList<>(); LateParameterizedType lpt = asLateParameterizedType(type); Class clazz = erase(type); BeanInfo bi = Introspector.getBeanInfo(clazz); for (PropertyDescriptor pd : bi.getPropertyDescriptors()) { if (pd instanceof IndexedPropertyDescriptor) { // Ignore index properties. continue; } Method getter = pd.getReadMethod(); if (getter == null || getter.getDeclaringClass() != clazz) { // Ignore properties not declared on the current type or not readable. continue; } // If the type we are processing has type variable bindings, then replace the type variables, if any. // For example, "T Foo#getId()" becomes "String Foo#getId()" if current type is Foo. Type returnType = getter.getGenericReturnType(); if (lpt != null) { returnType = lpt.resolve(returnType); } results.add(new SimpleGrainProperty(pd.getName(), returnType, flagsFor(pd))); } return results; } /** * Collects all grain properties defined on the specified type and all super classes and super interfaces. This * method uses the JavaBean introspector to identify the properties. Type variables are replaced with their * appropriate type arguments, if any. Classes are traversed breadth-first, with the super class traversed before * any super interfaces. * * @param type the type to introspect. * @return a list of grain properties. * @throws IntrospectionException if an exception occurs during introspection. */ static List collectProperties(Type type) throws IntrospectionException { List results = new ArrayList<>(); Set visited = new HashSet<>(); visited.add(null); // ensures we don't visit null superclass visited.add(Object.class); // ensures we don't visit Object and its confusing getClass() accessor. Deque workList = new LinkedList<>(); workList.add(type); while (!workList.isEmpty()) { Type current = workList.removeFirst(); if (visited.contains(current)) { continue; } visited.add(current); results.addAll(collectDeclaredProperties(current)); workList.add(genericSuperclassOf(current)); Collections.addAll(workList, genericInterfacesOf(current)); } return results; } /** * Returns all unique properties by name from the specified collection. For any two properties with the same * name, the property having the more specific type is preferred. * * @param properties the properties to scan for duplicates. * @return the most specific, unique properties by name. */ static List resolveProperties(Collection properties) { Map bestFit = new LinkedHashMap<>(); for (GrainProperty candidate : properties) { String name = candidate.getName(); GrainProperty previous = bestFit.put(name, candidate); if (previous != null) { // We have a collision/duplicate. If the candidate is wider than the previous, put the previous back. if (isWider(candidate.getType(), previous.getType())) { bestFit.put(name, previous); } } } return new ArrayList<>(bestFit.values()); } GrainSymbol buildGrainSymbol() throws IntrospectionException { if (schema.getTypeParameters().length > 0) { throw new IllegalArgumentException("Generic type grain generation is not supported."); } int typeTokenIndex = 0; Map typeTokens = new LinkedHashMap<>(); List properties = resolveProperties(collectProperties(schema)); Collections.sort(properties, GrainPropertyComparator.INSTANCE); List symbols = new ArrayList<>(); for (GrainProperty prop : properties) { Type immutableType = dewildcard(immutify(cook(prop.getType()))); GrainProperty immutableProp = new SimpleGrainProperty(prop.getName(), immutableType, prop.getFlags()); TypeSymbol immutableTypeSymbol = new TypeSymbol(immutableType, printerFactory); TypeTokenSymbol typeTokenSymbol = null; if (immutableType instanceof ParameterizedType) { typeTokenSymbol = typeTokens.get(immutableType); if (typeTokenSymbol == null) { int index = typeTokenIndex++; typeTokens.put( immutableType, typeTokenSymbol = new TypeTokenSymbol( "$token" + index, immutableTypeSymbol, new FieldSymbol("$transform" + index, immutableTypeSymbol))); } } symbols.add(new PropertySymbol(immutableProp, printerFactory, typeTokenSymbol)); } Symbol typePolicyLoadExpression = null; if (!typeTokens.isEmpty()) { if (typePolicyMember instanceof Method) { typePolicyLoadExpression = new StaticMethodInvocationExpression((Method)typePolicyMember, printerFactory); } else if (typePolicyMember instanceof Field) { typePolicyLoadExpression = new StaticFieldLoadExpression((Field)typePolicyMember, printerFactory); } } List superGrains = new ArrayList<>(); List superBuilders = new ArrayList<>(); for (Class superType : schema.getInterfaces()) { if (superType.isAnnotationPresent(GrainSchema.class)) { superGrains.add(new TypeSymbol(typeTable.getGrainClass(superType), printerFactory)); superBuilders.add(new TypeSymbol(typeTable.getGrainBuilderClass(superType), printerFactory)); } } return new GrainSymbol(superGrains, superBuilders, symbols, typeTokens.values(), typePolicyLoadExpression); } Map buildTypeSymbols() { Map map = new HashMap<>(); for (Map.Entry entry : typeTable.wellKnownTypes().entrySet()) { map.put(entry.getKey(), new TypeSymbol(entry.getValue(), printerFactory)); } for (Map.Entry entry : typeTable.schemaTypes(schema).entrySet()) { map.put(entry.getKey(), new TypeSymbol(entry.getValue(), printerFactory)); } return map; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy