/** * Copyright (C) 2010 Lowereast Software * * 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.lowereast.guiceymongo.data.generator; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import com.lowereast.guiceymongo.data.generator.property.BlobProperty; import com.lowereast.guiceymongo.data.generator.property.ListProperty; import com.lowereast.guiceymongo.data.generator.property.MapProperty; import com.lowereast.guiceymongo.data.generator.property.PrimitiveProperty; import com.lowereast.guiceymongo.data.generator.property.Property; import com.lowereast.guiceymongo.data.generator.property.SetProperty; import com.lowereast.guiceymongo.data.generator.property.UserDataProperty; import com.lowereast.guiceymongo.data.generator.property.UserEnumProperty; import com.lowereast.guiceymongo.data.generator.type.Type; import com.lowereast.guiceymongo.data.generator.type.UserDataType; import com.lowereast.guiceymongo.data.generator.type.UserEnumType; import com.lowereast.guiceymongo.data.generator.type.UserType; public class TypeGenerator { private final PrimitivePropertyGenerator _primitivePropertyGenerator; private final BlobPropertyGenerator _blobPropertyGenerator; private final ListPropertyGenerator _listPropertyGenerator; private final SetPropertyGenerator _setPropertyGenerator; private final MapPropertyGenerator _mapPropertyGenerator; private final UserEnumPropertyGenerator _userEnumTypePropertyGenerator; private final UserDataPropertyGenerator _userTypePropertyGenerator; public TypeGenerator(TypeRegistry registry) { _primitivePropertyGenerator = new PrimitivePropertyGenerator(registry); _blobPropertyGenerator = new BlobPropertyGenerator(registry); _listPropertyGenerator = new ListPropertyGenerator(registry); _setPropertyGenerator = new SetPropertyGenerator(registry); _mapPropertyGenerator = new MapPropertyGenerator(registry); _userEnumTypePropertyGenerator = new UserEnumPropertyGenerator(registry); _userTypePropertyGenerator = new UserDataPropertyGenerator(registry); } private Appendable appendIndent(Appendable builder, int indentCount) throws IOException { for (int x = 0; x < indentCount; ++x) builder.append("\t"); return builder; } private String getSimpleName(String typeName) { int index = typeName.lastIndexOf("."); if (index >= 0) typeName = typeName.substring(index + 1); return typeName; } private void create(String methodName, Appendable builder, UserDataType type, int indentCount) throws IOException { methodName = "create" + methodName; try { for (Property<?> property : type.getProperties()) { if (property instanceof PrimitiveProperty) PrimitivePropertyGenerator.class.getDeclaredMethod(methodName, Appendable.class, PrimitiveProperty.class, int.class).invoke(_primitivePropertyGenerator, builder, property, indentCount); else if (property instanceof BlobProperty) BlobPropertyGenerator.class.getDeclaredMethod(methodName, Appendable.class, BlobProperty.class, int.class).invoke(_blobPropertyGenerator, builder, property, indentCount); else if (property instanceof ListProperty) ListPropertyGenerator.class.getDeclaredMethod(methodName, Appendable.class, ListProperty.class, int.class).invoke(_listPropertyGenerator, builder, property, indentCount); else if (property instanceof SetProperty) SetPropertyGenerator.class.getDeclaredMethod(methodName, Appendable.class, SetProperty.class, int.class).invoke(_setPropertyGenerator, builder, property, indentCount); else if (property instanceof MapProperty) MapPropertyGenerator.class.getDeclaredMethod(methodName, Appendable.class, MapProperty.class, int.class).invoke(_mapPropertyGenerator, builder, property, indentCount); else if (property instanceof UserEnumProperty) UserEnumPropertyGenerator.class.getDeclaredMethod(methodName, Appendable.class, UserEnumProperty.class, int.class).invoke(_userEnumTypePropertyGenerator, builder, property, indentCount); else if (property instanceof UserDataProperty) UserDataPropertyGenerator.class.getDeclaredMethod(methodName, Appendable.class, UserDataProperty.class, int.class).invoke(_userTypePropertyGenerator, builder, property, indentCount); } } catch (IllegalArgumentException e) { e.printStackTrace(); } catch (SecurityException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } private void createUpdater(Appendable builder, UserDataType type, int indentCount) throws IOException { // find root type... UserDataType rootType = type; while (rootType.getParentType() != null) rootType = rootType.getParentType(); if (rootType.getIdentityProperty() == null) return; appendIndent(builder, indentCount).append("public static class Updater extends ").append(getSimpleName(type.getJavaType())).append(" implements com.lowereast.guiceymongo.data.IsUpdatable<").append(getSimpleName(type.getJavaType())).append("> {\n"); // member variables if (type.getParentType() != null) appendIndent(builder, indentCount + 1).append("private final ").append(type.getParentType().getJavaType()).append(".Updater _parent;\n"); appendIndent(builder, indentCount + 1).append("private final Wrapper _wrapper;\n"); appendIndent(builder, indentCount + 1).append("private final Builder _builder;\n"); if (type.getParentType() == null) appendIndent(builder, indentCount + 1).append("private final com.mongodb.DBCollection _collection;\n"); // constructor if (type.getParentType() == null) { appendIndent(builder, indentCount + 1).append("private Updater(Wrapper wrapper, com.mongodb.DBCollection collection) {\n"); appendIndent(builder, indentCount + 2).append("_wrapper = wrapper;\n"); appendIndent(builder, indentCount + 2).append("_builder = ").append(type.getJavaType()).append(".newBuilder();\n"); appendIndent(builder, indentCount + 2).append("_collection = collection;\n"); } else { appendIndent(builder, indentCount + 1).append("private Updater(").append(type.getParentType().getJavaType()).append(".Updater parent, Wrapper wrapper, Builder builder) {\n"); appendIndent(builder, indentCount + 2).append("_parent = parent;\n"); appendIndent(builder, indentCount + 2).append("_wrapper = wrapper != null ? wrapper : ").append(type.getJavaType()).append(".wrap(new com.mongodb.BasicDBObject());\n"); appendIndent(builder, indentCount + 2).append("_builder = builder != null ? builder : ").append(type.getJavaType()).append(".newBuilder();\n"); } appendIndent(builder, indentCount + 1).append("}\n"); // updater methods create("UpdaterMethod", builder, type, indentCount + 1); // buildUpdate if (type.getParentType() == null) { appendIndent(builder, indentCount + 1).append("public com.mongodb.DBObject buildUpdate() {\n"); appendIndent(builder, indentCount + 2).append("com.mongodb.DBObject dbObject = new com.mongodb.BasicDBObject();\n"); appendIndent(builder, indentCount + 2).append("buildUpdate(dbObject, \"\");\n"); appendIndent(builder, indentCount + 2).append("if (dbObject.keySet().size() == 0)\n"); appendIndent(builder, indentCount + 3).append("return null;\n"); appendIndent(builder, indentCount + 2).append("return new com.mongodb.BasicDBObject(\"$set\", dbObject);\n"); appendIndent(builder, indentCount + 1).append("}\n"); } appendIndent(builder, indentCount + 1).append("private void buildUpdate(com.mongodb.DBObject dbObject, String path) {\n"); create("UpdaterBuildUpdate", builder, type, indentCount + 2); appendIndent(builder, indentCount + 1).append("}\n"); // update if (type.getParentType() == null) { appendIndent(builder, indentCount + 1).append("public void update() {\n"); appendIndent(builder, indentCount + 2).append("com.mongodb.DBObject updateObject = buildUpdate();\n"); appendIndent(builder, indentCount + 2).append("if (updateObject != null)\n"); appendIndent(builder, indentCount + 3).append("_collection.update(new com.mongodb.BasicDBObject(").append(type.getIdentityProperty().getKeyName()).append(", get").append(type.getIdentityProperty().getCamelCaseName()).append("()), updateObject);\n"); appendIndent(builder, indentCount + 1).append("}\n"); } else { appendIndent(builder, indentCount + 1).append("public void update() {\n"); appendIndent(builder, indentCount + 2).append("_parent.update();\n"); appendIndent(builder, indentCount + 1).append("}\n"); } appendIndent(builder, indentCount).append("}\n"); if (type.getParentType() == null) { appendIndent(builder, indentCount).append("public static Updater newUpdater(com.mongodb.DBObject backing, com.mongodb.DBCollection collection) {\n"); appendIndent(builder, indentCount + 1).append("if (backing != null && collection != null)\n"); appendIndent(builder, indentCount + 2).append("return null;\n"); appendIndent(builder, indentCount + 1).append("return new Updater(").append(type.getJavaType()).append(".wrap(backing), collection);\n"); appendIndent(builder, indentCount).append("}\n"); } } private void createBuilder(Appendable builder, UserDataType type, int indentCount) throws IOException { appendIndent(builder, indentCount).append("public static class Builder extends ").append(getSimpleName(type.getJavaType())).append(" implements com.lowereast.guiceymongo.data.IsBuilder<").append(getSimpleName(type.getJavaType())).append("> {\n"); // constructor appendIndent(builder, indentCount + 1).append("private Builder() {}\n"); create("BuilderMethod", builder, type, indentCount + 1); appendIndent(builder, indentCount + 1).append("public com.mongodb.DBObject build() {\n"); appendIndent(builder, indentCount + 2).append("com.mongodb.DBObject dbObject = new com.mongodb.BasicDBObject();\n"); create("BuilderBuild", builder, type, indentCount + 2); appendIndent(builder, indentCount + 2).append("return dbObject;\n"); appendIndent(builder, indentCount + 1).append("}\n"); appendIndent(builder, indentCount).append("}\n"); appendIndent(builder, indentCount).append("public static Builder newBuilder() {\n"); appendIndent(builder, indentCount + 1).append("return new Builder();\n"); appendIndent(builder, indentCount).append("}\n"); appendIndent(builder, indentCount).append("public static Builder newBuilder(").append(type.getJavaType()).append(" value) {\n"); appendIndent(builder, indentCount + 1).append("Builder builder = new Builder();\n"); create("BuilderNewBuilder", builder, type, indentCount + 1); appendIndent(builder, indentCount + 1).append("return builder;\n"); appendIndent(builder, indentCount).append("}\n"); } private void createWrapper(Appendable builder, UserDataType type, int indentCount) throws IOException { appendIndent(builder, indentCount).append("public static class Wrapper extends ").append(getSimpleName(type.getJavaType())).append(" implements com.lowereast.guiceymongo.data.IsWrapper<").append(getSimpleName(type.getJavaType())).append("> {\n"); // member variable appendIndent(builder, indentCount + 1).append("private com.mongodb.DBObject _backing;\n"); builder.append("\n"); // constructor appendIndent(builder, indentCount + 1).append("private Wrapper(com.mongodb.DBObject backing) {\n"); appendIndent(builder, indentCount + 2).append("_backing = backing;\n"); appendIndent(builder, indentCount + 1).append("}\n"); create("WrapperMethod", builder, type, indentCount + 1); appendIndent(builder, indentCount + 1).append("public com.mongodb.DBObject getDBObject() {\n"); appendIndent(builder, indentCount + 2).append("return _backing;\n"); appendIndent(builder, indentCount + 1).append("}\n"); // appendIndent(builder, indentCount + 1).append("public ").append(getSimpleName(type.getJavaType())).append(" getWrapped() {\n"); // appendIndent(builder, indentCount + 2).append("return this;\n"); // appendIndent(builder, indentCount + 1).append("}\n"); appendIndent(builder, indentCount).append("}\n"); appendIndent(builder, indentCount).append("public static com.lowereast.guiceymongo.data.DataWrapper<").append(type.getJavaType()).append("> DataWrapper = new com.lowereast.guiceymongo.data.DataWrapper<").append(type.getJavaType()).append(">() {\n"); appendIndent(builder, indentCount + 1).append("public ").append(type.getJavaType()).append(".Wrapper wrap(com.mongodb.DBObject backing) {\n"); appendIndent(builder, indentCount + 2).append("return ").append(type.getJavaType()).append(".wrap(backing);\n"); appendIndent(builder, indentCount + 1).append("}\n"); appendIndent(builder, indentCount).append("};\n"); appendIndent(builder, indentCount).append("public static ").append(type.getJavaType()).append(".Wrapper wrap(com.mongodb.DBObject backing) {\n"); appendIndent(builder, indentCount + 1).append("if (backing == null)\n"); appendIndent(builder, indentCount + 2).append("return null;\n"); appendIndent(builder, indentCount + 1).append("return new ").append(type.getJavaType()).append(".Wrapper(backing);\n"); appendIndent(builder, indentCount).append("}\n"); appendIndent(builder, indentCount).append("public static ").append(type.getJavaType()).append(".Wrapper convertFrom(com.lowereast.guiceymongo.data.IsWrapper<?> wrapped) {\n"); appendIndent(builder, indentCount + 1).append("if (wrapped == null)\n"); appendIndent(builder, indentCount + 2).append("return null;\n"); appendIndent(builder, indentCount + 1).append("return new ").append(type.getJavaType()).append(".Wrapper(wrapped.getDBObject());\n"); appendIndent(builder, indentCount).append("}\n"); appendIndent(builder, indentCount).append("public static ").append(type.getJavaType()).append(".Wrapper convertFrom(com.lowereast.guiceymongo.data.IsData data) {\n"); appendIndent(builder, indentCount + 1).append("if (data == null || !(data instanceof com.lowereast.guiceymongo.data.IsWrapper<?>))\n"); appendIndent(builder, indentCount + 2).append("return null;\n"); appendIndent(builder, indentCount + 1).append("return new ").append(type.getJavaType()).append(".Wrapper(((com.lowereast.guiceymongo.data.IsWrapper<?>)data).getDBObject());\n"); appendIndent(builder, indentCount).append("}\n"); } private void createEnumType(Appendable builder, UserEnumType type, int indentCount, boolean innerClass) throws IOException { appendIndent(builder, indentCount).append("/**\n"); appendIndent(builder, indentCount).append(" * ").append(type.getComment()).append('\n'); appendIndent(builder, indentCount).append(" */\n"); appendIndent(builder, indentCount).append("public " + (innerClass ? "static " : "") + "enum ").append(getSimpleName(type.getJavaType())).append(" {\n"); List<String> values = new ArrayList<String>(type.getValues()); for (int x = 0; x < values.size(); ++x) { appendIndent(builder, indentCount + 1).append(values.get(x)); if (x < values.size() - 1) builder.append(","); builder.append("\n"); } appendIndent(builder, indentCount).append("}\n"); } private void createUserType(Appendable builder, UserDataType type, int indentCount, boolean innerClass) throws IOException { appendIndent(builder, indentCount).append("/**\n"); appendIndent(builder, indentCount).append(" * ").append(type.getComment()).append('\n'); appendIndent(builder, indentCount).append(" */\n"); appendIndent(builder, indentCount).append("public " + (innerClass ? "static " : "") + "abstract class ").append(getSimpleName(type.getJavaType())).append(" implements com.lowereast.guiceymongo.data.IsData {\n"); create("Key", builder, type, indentCount + 1); builder.append("\n"); create("ReadableMethod", builder, type, indentCount + 1); builder.append("\n"); createWrapper(builder, type, indentCount + 1); createBuilder(builder, type, indentCount + 1); // createUpdater(builder, type, indentCount + 1); appendIndent(builder, indentCount + 1).append("@Override public boolean equals(Object obj) {\n"); appendIndent(builder, indentCount + 2).append("if (obj == null || !(obj instanceof ").append(getSimpleName(type.getJavaType())).append("))\n"); appendIndent(builder, indentCount + 3).append("return false;\n"); if (type.getProperties().size() > 0) { appendIndent(builder, indentCount + 2).append(getSimpleName(type.getJavaType())).append(" other = (").append(getSimpleName(type.getJavaType())).append(")obj;\n"); create("Equals", builder, type, indentCount + 1); } appendIndent(builder, indentCount + 2).append("return true;\n"); appendIndent(builder, indentCount + 1).append("}\n"); for (Type childType : type.getChildTypes()) { if (childType instanceof UserDataType) createUserType(builder, (UserDataType)childType, indentCount + 1, true); else if (childType instanceof UserEnumType) createEnumType(builder, (UserEnumType)childType, indentCount + 1, true); } appendIndent(builder, indentCount).append("}\n"); } public void generate(Appendable builder, UserType type) throws IOException { builder.append("// Generated file!!! DO NOT EDIT THIS!!!\n\n"); if (type instanceof UserDataType) createUserType(builder, (UserDataType)type, 0, false); else if (type instanceof UserEnumType) createEnumType(builder, (UserEnumType)type, 0, false); } }