Start line:  
End line:  

Snippet Preview

Snippet HTML Code

Stack Overflow Questions
  /*
   * Copyright 2007 Daniel Spiewak
   *
   * 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.java.ao.schema;
 
 import java.sql.Date;
 import java.sql.Types;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
 
 
 import static com.google.common.collect.Iterables.addAll;
 import static net.java.ao.types.TypeQualifiers.MAX_STRING_LENGTH;
 import static net.java.ao.types.TypeQualifiers.qualifiers;

WARNING: Not part of the public API. This class is public only to allow its use within other packages in the ActiveObjects library.

Author(s):
Daniel Spiewak
 
 public final class SchemaGenerator
 {
     private static final Logger logger = LoggerFactory.getLogger(SchemaGenerator.class);
 
     private static final Set<IntegerAUTO_INCREMENT_LEGAL_TYPES = ImmutableSet.of(..);
 
     public static void migrate(DatabaseProvider provider,
                                SchemaConfiguration schemaConfiguration,
                                NameConverters nameConverters,
                                final boolean executeDestructiveUpdates,
                                Class<? extends RawEntity<?>>... classesthrows SQLException
     {
         final Iterable<Iterable<SQLAction>> actionGroups = generateImpl(providerschemaConfigurationnameConvertersexecuteDestructiveUpdatesclasses);
         final Connection conn = provider.getConnection();
         try
         {
             final Statement stmt = conn.createStatement();
             try
             {
                 Set<StringcompletedStatements = new HashSet<String>();
                 for (Iterable<SQLActionactionGroup : actionGroups)
                 {
                     addAll(completedStatementsprovider.executeUpdatesForActions(stmtactionGroupcompletedStatements));
                 }
            }
            finally
            {
                stmt.close();
            }
        }
        finally
        {
            conn.close();
        }
    }
    private static Iterable<Iterable<SQLAction>> generateImpl(final DatabaseProvider provider,
                                                 final SchemaConfiguration schemaConfiguration,
                                                 final NameConverters nameConverters,
                                                 final boolean executeDestructiveUpdates,
                                                 Class<? extends RawEntity<?>>... classesthrows SQLException
    {
        final DDLTable[] parsedTables = parseDDL(providernameConvertersclasses);
        final DDLTable[] readTables = SchemaReader.readSchema(providernameConvertersschemaConfiguration);
        final DDLAction[] actions = SchemaReader.sortTopologically(SchemaReader.diffSchema(provider.getTypeManager(), parsedTablesreadTablesprovider.isCaseSensitive()));
        return Iterables.transform(Iterables.filter(ImmutableList.copyOf(actions), new Predicate<DDLAction>() {
            @Override
            public boolean apply(final DDLAction input) {
                switch (input.getActionType()) {
                    case :
                    case :
                        return executeDestructiveUpdates;
                    default:
                        return true;
                }
            }
        }),
                new Function<DDLActionIterable<SQLAction>>()
                {
                    public Iterable<SQLActionapply(DDLAction from)
                    {
                        return provider.renderAction(nameConvertersfrom);
                    }
                });
    }
    static DDLTable[] parseDDL(DatabaseProvider providerNameConverters nameConvertersClass<? extends RawEntity<?>>... classes) {
		final Map<Class<? extends RawEntity<?>>, Set<Class<? extends RawEntity<?>>>> deps = new HashMap<Class<? extends RawEntity<?>>, Set<Class<? extends RawEntity<?>>>>();
		final Set<Class<? extends RawEntity<?>>> roots = new LinkedHashSet<Class<? extends RawEntity<?>>>();
        for (Class<? extends RawEntity<?>> cls : classes) {
            parseDependencies(nameConverters.getFieldNameConverter(), depsrootscls);
        }
        ArrayList<DDLTableparsedTables = new ArrayList<DDLTable>();
        parseDDLRoots(providernameConvertersdepsrootsparsedTables);
        if (!deps.isEmpty()) {
            throw new RuntimeException("Circular dependency detected");
        }
		return parsedTables.toArray(new DDLTable[parsedTables.size()]);
	}
    private static void parseDDLRoots(final DatabaseProvider provider,
                                      final NameConverters nameConverters,
                                      final Map<Class<? extends RawEntity<?>>, Set<Class<? extends RawEntity<?>>>> deps,
                                      final Set<Class<? extends RawEntity<?>>> roots,
                                      final ArrayList<DDLTableparsedTables) {
        while (!roots.isEmpty()) {
            Class<? extends RawEntity<?>> clazz = roots.iterator().next();
            roots.remove(clazz);
            if (clazz.getAnnotation(Polymorphic.class) == null) {
                parsedTables.add(parseInterface(providernameConverters.getTableNameConverter(), nameConverters.getFieldNameConverter(), clazz));
            }
            List<Class<? extends RawEntity<?>>> toRemove = new LinkedList<Class<? extends RawEntity<?>>>();
            for (final Class<? extends RawEntity<?>> depClass : deps.keySet()) {
                Set<Class<? extends RawEntity<?>>> individualDeps = deps.get(depClass);
                individualDeps.remove(clazz);
                if (individualDeps.isEmpty()) {
                    roots.add(depClass);
                    toRemove.add(depClass);
                }
            }
            for (Class<? extends RawEntity<?>> remove : toRemove) {
                deps.remove(remove);
            }
        }
    }
    private static void parseDependencies(
            final FieldNameConverter fieldConverter,
            final Map<Class<? extends RawEntity<?>>, Set<Class<? extends RawEntity<?>>>> deps,
            final Set<Class<? extends RawEntity<?>>> rootsClass<? extends RawEntity<?>> clazz)
    {
        if (deps.containsKey(clazz))
        {
            return;
        }
        final Set<Class<? extends RawEntity<?>>> individualDeps = new LinkedHashSet<Class<? extends RawEntity<?>>>();
        for (final Method method : clazz.getMethods())
        {
            final Class<?> type = Common.getAttributeTypeFromMethod(method);
            validateManyToManyAnnotation(method);
            validateOneToOneAnnotation(method);
            validateOneToManyAnnotation(method);
            if (fieldConverter.getName(method) != null && type != null && !type.equals(clazz) &&
                RawEntity.class.isAssignableFrom(type) && !individualDeps.contains(type))
            {
                individualDeps.add((Class<? extends RawEntity<?>>) type);
                addDeps(depsclazzindividualDeps);
                parseDependencies(fieldConverterdepsroots, (Class<? extends RawEntity<?>>) type);
            }
        }
        if (individualDeps.size() == 0)
        {
            roots.add(clazz);
        }
        else
        {
            addDeps(depsclazzindividualDeps);
        }
    }
    private static void addDeps(final Map<Class<? extends RawEntity<?>>, Set<Class<? extends RawEntity<?>>>> deps,
                                final Class<? extends RawEntity<?>> clazz,
                                final Set<Class<? extends RawEntity<?>>> individualDeps)
    {
        Set<Class<? extends RawEntity<?>>> classes = deps.get(clazz);
        if (classes != null) {
            classes.addAll(individualDeps);
        } else {
            deps.put(clazzindividualDeps);
        }
    }
    private static void validateManyToManyAnnotation(final Method method)
    {
        final ManyToMany manyToMany = method.getAnnotation(ManyToMany.class);
        if (manyToMany != null)
        {
            final Class<? extends RawEntity<?>> throughType = manyToMany.value();
            final String reverse = manyToMany.reverse();
            if (reverse.length() == 0)
            {
                .warn(method + " does not have a value specified for the reverse element of its ManyToMany annotation. A value will be required by a future version of ActiveObjects.");
            }
            else
            {
                try
                {
                    throughType.getMethod(reverse);
                }
                catch (final NoSuchMethodException exception)
                {
                    throw new IllegalArgumentException(method + " has a ManyToMany annotation with an invalid reverse element value. It must be the name of the corresponding getter method on the joining entity."exception);
                }
            }
            if (manyToMany.through().length() == 0)
            {
                .warn(method + " does not have a value specified for the through element of its ManyToMany annotation. A value will be required by a future version of ActiveObjects.");
            } else {
                try
                {
                    throughType.getMethod(manyToMany.through());
                }
                catch (final NoSuchMethodException exception)
                {
                    throw new IllegalArgumentException(method + " has a ManyToMany annotation with an invalid through element value. It must be the name of the getter method on the joining entity that refers to the remote entities."exception);
                }
            }
        }
    }
    private static void validateOneToManyAnnotation(final Method method)
    {
        final OneToMany oneToMany = method.getAnnotation(OneToMany.class);
        if (oneToMany != null)
        {
            final String reverse = oneToMany.reverse();
            if (reverse.length() == 0)
            {
                .warn(method + " does not have a value specified for the reverse element of its OneToMany annotation. A value will be required by a future version of ActiveObjects.");
            }
            else
            {
                try
                {
                    method.getReturnType().getComponentType().getMethod(reverse);
                }
                catch (final NoSuchMethodException exception)
                {
                    throw new IllegalArgumentException(method + " has a OneToMany annotation with an invalid reverse element value. It must be the name of the corresponding getter method on the related entity."exception);
                }
            }
        }
    }
    private static void validateOneToOneAnnotation(final Method method)
    {
        final OneToOne oneToOne = method.getAnnotation(OneToOne.class);
        if (oneToOne != null)
        {
            final String reverse = oneToOne.reverse();
            if (reverse.length() == 0)
            {
                .warn(method + " does not have a value specified for the reverse element of its OneToOne annotation. A value will be required by a future version of ActiveObjects.");
            }
            else
            {
                try
                {
                    method.getReturnType().getMethod(reverse);
                }
                catch (final NoSuchMethodException exception)
                {
                    throw new IllegalArgumentException(method + " has OneToMany annotation with an invalid reverse element value. It be the name of the corresponding getter method on the related entity."exception);
                }
            }
        }
    }

    
Not intended for public use.
    public static DDLTable parseInterface(DatabaseProvider providerTableNameConverter nameConverterFieldNameConverter fieldConverterClass<? extends RawEntity<?>> clazz)
    {
		String sqlName = nameConverter.getName(clazz);
		DDLTable table = new DDLTable();
		table.setName(sqlName);
		table.setFields(parseFields(providerfieldConverterclazz));
		table.setForeignKeys(parseForeignKeys(nameConverterfieldConverterclazz));
		table.setIndexes(parseIndexes(providernameConverterfieldConverterclazz));
		return table;
	}

Not intended for public usage. This method is declared public only to enable use within other ActiveObjects packages. Consider this function unsupported.
	public static DDLField[] parseFields(DatabaseProvider providerFieldNameConverter fieldConverterClass<? extends RawEntity<?>> clazz) {
		List<DDLFieldfields = new ArrayList<DDLField>();
		List<Stringattributes = new LinkedList<String>();
		for (Method method : Common.getValueFieldsMethods(clazzfieldConverter))
        {
			String attributeName = fieldConverter.getName(method);
			final Class<?> type = Common.getAttributeTypeFromMethod(method);
			if (attributeName != null && type != null)
            {
                checkIsSupportedType(methodtype);
				if (attributes.contains(attributeName)) {
					continue;
				}
				attributes.add(attributeName);
                final AnnotationDelegate annotations = Common.getAnnotationDelegate(fieldConvertermethod);
                DDLField field = new DDLField();
                field.setName(attributeName);
                final TypeManager typeManager = provider.getTypeManager();
                final TypeInfo<?> sqlType = getSQLTypeFromMethod(typeManagertypemethodannotations);
                field.setType(sqlType);
                field.setJdbcType(sqlType.getJdbcWriteType());
                field.setPrimaryKey(isPrimaryKey(annotationsfield));
                field.setNotNull(annotations.isAnnotationPresent(NotNull.class) || annotations.isAnnotationPresent(Unique.class) || annotations.isAnnotationPresent(PrimaryKey.class));
                field.setUnique(annotations.isAnnotationPresent(Unique.class));
                final boolean isAutoIncrement = isAutoIncrement(typeannotationsfield.getType());
                field.setAutoIncrement(isAutoIncrement);
                if (!isAutoIncrement)
                {
                    if (annotations.isAnnotationPresent(Default.class))
                    {
                        final Object defaultValue = convertStringDefaultValue(annotations.getAnnotation(Default.class).value(), sqlTypemethod);
                        if (type.isEnum() && ((IntegerdefaultValue) > EnumUtils.size((Class<? extends Enum>) type) - 1)
                        {
                            throw new ActiveObjectsConfigurationException("There is no enum value of '" + type + "'for which the ordinal is " + defaultValue);
                        }
                        field.setDefaultValue(defaultValue);
                    }
                    else if (ImmutableSet.<Class<?>>of(short.classfloat.classint.classlong.classdouble.class).contains(type))
                    {
                        // set the default value for primitive types (float, short, int, long, char)
                        field.setDefaultValue(convertStringDefaultValue("0"sqlTypemethod));
                    }
                }
                if (field.isPrimaryKey()) {
					fields.add(0, field);
else {
					fields.add(field);
				}
                if (RawEntity.class.isAssignableFrom(type)
						&& type.getAnnotation(Polymorphic.class) != null) {
					field.setDefaultValue(null);		// polymorphic fields can't have default
					attributeName = fieldConverter.getPolyTypeName(method);
					field = new DDLField();
					field.setName(attributeName);
					field.setType(typeManager.getType(String.classqualifiers().stringLength(127)));
                    field.setJdbcType(...);
					if (annotations.getAnnotation(NotNull.class) != null) {
						field.setNotNull(true);
					}
					fields.add(field);
				}
			}
		}
		return fields.toArray(new DDLField[fields.size()]);
	}
    private static void checkIsSupportedType(Method methodClass<?> type)
    {
        if (type.equals(java.sql.Date.class))
        {
            throw new ActiveObjectsConfigurationException(Date.class.getName()
                    + " is not supported! Please use " + java.util.Date.class.getName() + " instead.")
                    .forMethod(method);
        }
    }
    private static boolean isPrimaryKey(AnnotationDelegate annotationsDDLField field)
    {
        final boolean isPrimaryKey = annotations.isAnnotationPresent(PrimaryKey.class);
        if (isPrimaryKey && !field.getType().isAllowedAsPrimaryKey())
        {
            throw new ActiveObjectsConfigurationException(PrimaryKey.class.getName() + " is not supported for type: " + field.getType());
        }
        return isPrimaryKey;
    }
    private static boolean isAutoIncrement(Class<?> typeAnnotationDelegate annotationsTypeInfo<?> dbType)
    {
        final boolean isAutoIncrement = annotations.isAnnotationPresent(AutoIncrement.class);
        if (isAutoIncrement && (!.contains(dbType.getJdbcWriteType()) || type.isEnum()))
        {
            throw new ActiveObjectsConfigurationException(AutoIncrement.class.getName() + " is not supported for type: " + dbType);
        }
        return isAutoIncrement;
    }
    private static TypeInfo<?> getSQLTypeFromMethod(TypeManager typeManagerClass<?> typeMethod methodAnnotationDelegate annotations) {
		TypeQualifiers qualifiers = qualifiers();
		StringLength lengthAnno = annotations.getAnnotation(StringLength.class);
		if (lengthAnno != null) {
		    final int length = lengthAnno.value();
		    if (length > .)
		    {
		        if (length > .)
                {
                    throw new ActiveObjectsConfigurationException("@StringLength must be <= " +  + " or UNLIMITED").forMethod(method);
                }
                else
                {
                    .warn("@StringLength is {}. Since 0.28.4 the suggested max string length has been reduced to 450 characters due to a limitation in MS SQL Server."
                            +" Starting from 0.29.0 exceeding this maximum string length will throw an Exception"length);
                }
		    }
		    try {
		        qualifiers = qualifiers.stringLength(length);
		    }
		        throw new ActiveObjectsConfigurationException(e.getMessage()).forMethod(method);
		    }
		}
		return typeManager.getType(typequalifiers);
	}
    private static DDLForeignKey[] parseForeignKeys(TableNameConverter nameConverterFieldNameConverter fieldConverter,
			Class<? extends RawEntity<?>> clazz) {
		for (Method method : clazz.getMethods()) {
			String attributeName = fieldConverter.getName(method);
			Class<?> type =  Common.getAttributeTypeFromMethod(method);
            if (type != null && attributeName != null && RawEntity.class.isAssignableFrom(type)
					&& type.getAnnotation(Polymorphic.class) == null) {
				key.setField(attributeName);
				key.setTable(nameConverter.getName((Class<? extends RawEntity<?>>) type));
				key.setForeignField(Common.getPrimaryKeyField((Class<? extends RawEntity<?>>) typefieldConverter));
				key.setDomesticTable(nameConverter.getName(clazz));
				back.add(key);
			}
		}
		return back.toArray(new DDLForeignKey[back.size()]);
	}
	static DDLIndex[] parseIndexes(DatabaseProvider providerTableNameConverter nameConverterFieldNameConverter fieldConverter,
                                           Class<? extends RawEntity<?>> clazz) {
		Set<DDLIndexback = new LinkedHashSet<DDLIndex>();
		String tableName = nameConverter.getName(clazz);
		for (Method method : clazz.getMethods()) {
			String attributeName = fieldConverter.getName(method);
			AnnotationDelegate annotations = Common.getAnnotationDelegate(fieldConvertermethod);
			if (Common.isAccessor(method) || Common.isMutator(method)) {
				Indexed indexedAnno = annotations.getAnnotation(Indexed.class);
				Class<?> type = Common.getAttributeTypeFromMethod(method);
                if (indexedAnno != null || (type != null && RawEntity.class.isAssignableFrom(type))) {
					DDLIndex index = new DDLIndex();
					index.setField(attributeName);
					index.setTable(tableName);
					index.setType(getSQLTypeFromMethod(provider.getTypeManager(), typemethodannotations));
					back.add(index);
				}
			}
		}
		for (Class<?> superInterface : clazz.getInterfaces()) {
			if (RawEntity.class.isAssignableFrom(superInterface) &&
                    !(RawEntity.class.equals(superInterface) || superInterface.isAnnotationPresent(Polymorphic.class))) {
                        provider,
                        nameConverter,
                        fieldConverter,
                        (Class<? extends RawEntity<?>>) superInterface)
                ));
			}
		}
		return back.toArray(new DDLIndex[back.size()]);
	}
    private static Object convertStringDefaultValue(String valueTypeInfo<?> typeMethod method)
    {
        if (value == null)
        {
            return null;
        }
        if (!type.getSchemaProperties().isDefaultValueAllowed())
        {
            throw new ActiveObjectsConfigurationException("Default value is not allowed for database type " +
                type.getSchemaProperties().getSqlTypeName());
        }
        try
        {
            Object ret = type.getLogicalType().parseDefault(value);
            if (ret == null)
            {
                throw new ActiveObjectsConfigurationException("Default value cannot be empty").forMethod(method);
            }
            return ret;
        }
        catch (IllegalArgumentException e)
        {
            throw new ActiveObjectsConfigurationException(e.getMessage());
        }
    }
New to GrepCode? Check out our FAQ X