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

org.bndly.schema.beans.SchemaBeanGeneratorMojo Maven / Gradle / Ivy

The newest version!
package org.bndly.schema.beans;

/*-
 * #%L
 * Maven Schema Bean Generator
 * %%
 * Copyright (C) 2013 - 2020 Cybercon GmbH
 * %%
 * 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.
 * #L%
 */

import org.bndly.code.common.CodeGenerationContext;
import org.bndly.code.model.CodeBlock;
import org.bndly.code.model.CodeBracket;
import org.bndly.code.model.CodeImport;
import org.bndly.code.model.CodeLine;
import org.bndly.code.model.CodePackage;
import org.bndly.code.output.JavaCodeElementStringWriter;
import org.bndly.code.renderer.ImportResolver;
import org.bndly.schema.model.Attribute;
import org.bndly.schema.model.BinaryAttribute;
import org.bndly.schema.model.BooleanAttribute;
import org.bndly.schema.model.CryptoAttribute;
import org.bndly.schema.model.DateAttribute;
import org.bndly.schema.model.DecimalAttribute;
import org.bndly.schema.model.InverseAttribute;
import org.bndly.schema.model.JSONAttribute;
import org.bndly.schema.model.Mixin;
import org.bndly.schema.model.NamedAttributeHolder;
import org.bndly.schema.model.NamedAttributeHolderAttribute;
import org.bndly.schema.model.Schema;
import org.bndly.schema.model.StringAttribute;
import org.bndly.schema.model.Type;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Mojo;

/**
 *  Generates Schema Beans from a predefined Schema
 */
@Mojo(
		name = "generate"
)
public class SchemaBeanGeneratorMojo extends AbstractSchemaBasedBeanGeneratorMojo {

	@Override
	protected void doCodeGenerationWithSchema(Schema schema, CodeGenerationContext ctx, File targetPath) throws IOException, MojoExecutionException, MojoFailureException {
		ctx.setImportResolver(new ImportResolver() {
			@Override
			public String resolveImport(String simpleName) {
				if (simpleName == null) {
					throw new IllegalArgumentException("can not resolve import when simpleName is null.");
				}
				return simpleName;
			}
		});

		List types = schema.getTypes();
		if (types != null) {
			for (Type type : types) {
				importStateHolder.reset();
				CodeBlock code = generateCodeForType(type, ctx);
				writeCodeToFile(code, targetPath, type);
			}
		}
		List mixins = schema.getMixins();
		if (mixins != null) {
			for (Mixin mixin : mixins) {
				importStateHolder.reset();
				CodeBlock code = generateCodeForMixin(mixin, ctx);
				writeCodeToFile(code, targetPath, mixin);
			}
		}
	}

	private CodeBlock generateCodeForMixin(Mixin mixin, CodeGenerationContext ctx) {
		getLog().info("generating java interface for mixin " + mixin.getName());
		CodePackage block = ctx.create(CodePackage.class, ctx.getBasePackage());
		CodeBlock importBlock = block.createContained(CodeBlock.class);
		block.line(convertToJavaDoc(generateTypeJavaDoc(mixin), 0));
		block.line("public interface ").append(mixin.getName());
		CodeBracket bracket = block.createContained(CodeBracket.class);
		generateGetterAndSetter(mixin, bracket, importBlock);
		return block;
	}

	private CodeBlock generateCodeForType(Type type, CodeGenerationContext ctx) {
		getLog().info("generating java interface for type " + type.getName());
		CodeBlock block = ctx.create(CodeBlock.class);
		block.line("package ").append(ctx.getBasePackage()).append(";");
		CodeBlock importBlock = block.createContained(CodeBlock.class);
		block.line(convertToJavaDoc(generateTypeJavaDoc(type), 0));
		CodeLine l = block.line("public interface ").append(type.getName());
		boolean first = true;
		if (type.getMixins() != null) {
			for (Mixin mixin : type.getMixins()) {
				first = appendExtendedAttributeHolder(first, l, mixin);
			}
		}
		if (type.getSuperType() != null) {
			appendExtendedAttributeHolder(first, l, type.getSuperType());
		}
		CodeBracket bracket = block.createContained(CodeBracket.class);
		generateGetterAndSetter(type, bracket, importBlock);
		return block;
	}

	protected static String getJavaTypeNameForAttribute(Attribute attribute, CodeBlock importBlock, ImportStateHolder importStateHolder) {
		return getJavaTypeNameForAttribute(attribute, importBlock, importStateHolder, false);
	}

	protected static String getJavaTypeNameForAttribute(Attribute attribute, CodeBlock importBlock, ImportStateHolder importStateHolder, boolean importAttributeHolders) {
		if (StringAttribute.class.isInstance(attribute)) {
			return "String";
		} else if (CryptoAttribute.class.isInstance(attribute)) {
			return "String";
		} else if (DecimalAttribute.class.isInstance(attribute)) {
			DecimalAttribute att = DecimalAttribute.class.cast(attribute);
			Integer dp = att.getDecimalPlaces();
			if (dp == null) {
				dp = 0;
			}
			Integer length = att.getLength();
			if (length == null) {
				if (dp == 0) {
					return "Long";
				} else {
					return "Double";
				}
			} else {
				assertIsImported(importBlock, BigDecimal.class, importStateHolder);
				return "BigDecimal";
			}
		} else if (BooleanAttribute.class.isInstance(attribute)) {
			return "Boolean";
		} else if (DateAttribute.class.isInstance(attribute)) {
			assertIsImported(importBlock, Date.class, importStateHolder);
			return "Date";
		} else if (NamedAttributeHolderAttribute.class.isInstance(attribute)) {
			NamedAttributeHolderAttribute att = NamedAttributeHolderAttribute.class.cast(attribute);
			if (importAttributeHolders) {
				assertIsImported(importBlock, importBlock.getContext().getImportResolver().resolveImport(att.getNamedAttributeHolder().getName()), importStateHolder);
			}
			return att.getNamedAttributeHolder().getName();
		} else if (InverseAttribute.class.isInstance(attribute)) {
			InverseAttribute att = InverseAttribute.class.cast(attribute);
			assertIsImported(importBlock, List.class, importStateHolder);
			if (importAttributeHolders) {
				assertIsImported(importBlock, importBlock.getContext().getImportResolver().resolveImport(att.getReferencedAttributeHolder().getName()), importStateHolder);
			}
			return "List<" + att.getReferencedAttributeHolder().getName() + ">";
		} else if (JSONAttribute.class.isInstance(attribute)) {
			JSONAttribute att = JSONAttribute.class.cast(attribute);
			if (importAttributeHolders) {
				assertIsImported(importBlock, importBlock.getContext().getImportResolver().resolveImport(att.getNamedAttributeHolder().getName()), importStateHolder);
			}
			return att.getNamedAttributeHolder().getName();
		} else if (BinaryAttribute.class.isInstance(attribute)) {
			BinaryAttribute att = BinaryAttribute.class.cast(attribute);
			if (att.getAsByteArray() != null && att.getAsByteArray()) {
				return "byte[]";
			} else {
				assertIsImported(importBlock, InputStream.class, importStateHolder);
				return "InputStream";
			}
		}
		throw new IllegalArgumentException("unsupported attribute type");
	}

	private void generateGetterAndSetter(NamedAttributeHolder nah, CodeBracket bracket, CodeBlock importBlock) {
		if (nah.getAttributes() != null) {
			for (Attribute attribute : nah.getAttributes()) {
				String javaTypeName = getJavaTypeNameForAttribute(attribute, importBlock, importStateHolder);
				String ucAttributeName = upperCaseFirstLetter(attribute.getName());
				convertToJavaDoc(generateGetterJavaDoc(attribute), bracket);
				bracket.line(javaTypeName).append(" get").append(ucAttributeName).append("();");
				convertToJavaDoc(generateSetterJavaDoc(attribute), bracket);
				bracket.line("void set").append(ucAttributeName).append("(").append(javaTypeName).append(" ").append(attribute.getName()).append(");");
			}
		}
	}

	protected static String upperCaseFirstLetter(String string) {
		if (string.length() > 1) {
			string = string.substring(0, 1).toUpperCase() + string.substring(1);
		} else {
			string = string.toUpperCase();
		}
		return string;
	}

	private boolean appendExtendedAttributeHolder(boolean first, CodeLine l, NamedAttributeHolder nah) {
		if (first) {
			first = false;
			l.append(" extends ");
		} else {
			l.append(", ");
		}
		l.append(nah.getName());
		return first;
	}

	private void writeCodeToFile(CodeBlock code, File path, NamedAttributeHolder nah) throws IOException {
		String codeAsString = JavaCodeElementStringWriter.toString(code);
		File f = new File(path, nah.getName() + ".java");
		f.createNewFile();
		FileWriter w = new FileWriter(f);
		w.write(codeAsString);
		w.flush();
		w.close();
	}

	protected static void assertIsImported(CodeBlock importBlock, String typeToImport, ImportStateHolder importStateHolder) {
		if (!importStateHolder.hasBeenImported(typeToImport)) {
			importBlock.createContained(CodeImport.class, typeToImport);
		}
	}

	protected static void assertIsImported(CodeBlock importBlock, Class typeToImport, ImportStateHolder importStateHolder) {
		assertIsImported(importBlock, typeToImport.getName(), importStateHolder);
	}
	
/**
* Sets the value for the attribute 'city'.
* The attribute is declared as mandatory.
* The value is allowed to have a maximum of 255 characters.
* @param city 
*/
	static String generateSetterJavaDoc(Attribute attribute) {
		StringBuilder sb = new StringBuilder();
		sb.append("Sets the value for the attribute '").append(attribute.getName()).append("'.\n");
		if (attribute.isMandatory()) {
			sb.append("The attribute is declared as mandatory.\n");
		}
		if (attribute.isVirtual()) {
			sb.append("The attribute is declared as virtual. You can not use it in queries!\n");
		}
		if (StringAttribute.class.isInstance(attribute)) {
			StringAttribute sa = (StringAttribute) attribute;
			Integer length = sa.getLength();
			if (length != null) {
				sb.append("The value is allowed to have a maximum of ").append(length).append(" characters.\n");
			}
			if (sa.getIsLong() != null && sa.getIsLong()) {
				sb.append("The value is allowed to be long. This means it can not be used in queries.\n");
			}
		} else if (DecimalAttribute.class.isInstance(attribute)) {
			DecimalAttribute da = (DecimalAttribute) attribute;
			if (da.getLength() != null) {
				sb.append("The value is allowed to have ").append(da.getLength()).append(" digits (precision).\n");
			}
			if (da.getDecimalPlaces() != null) {
				sb.append("The value is allowed to have ").append(da.getDecimalPlaces()).append(" decimal places (scale).\n");
			}
		} else if (JSONAttribute.class.isInstance(attribute)) {
			sb.append("The value is stored as JSON data and can not be queried.\n");
		} else if (BinaryAttribute.class.isInstance(attribute)) {
			sb.append("The value is stored as a blob and can not be queried.\n");
		}
		String docs = attribute.getAnnotations().get("docs");
		if (docs != null) {
			sb.append(docs).append("\n");
		}
		sb.append("@param ").append(attribute.getName()).append(" value of '").append(attribute.getName()).append("'");
		return sb.toString();
	}
	
	static String generateGetterJavaDoc(Attribute attribute) {
		StringBuilder sb = new StringBuilder();
		sb.append("Returns the value for the attribute '").append(attribute.getName()).append("'.\n");
		if (attribute.isVirtual()) {
			sb.append("The attribute is declared virtual. Therefore your application has to provide logic, that implements the resolving of the attribute values.\n");
		}
		String docs = attribute.getAnnotations().get("docs");
		if (docs != null) {
			sb.append(docs).append("\n");
		}
		sb.append("@return value of '").append(attribute.getName()).append("'");
		return sb.toString();
	}
	
	static String generateTypeJavaDoc(NamedAttributeHolder namedAttributeHolder) {
		StringBuilder sb = new StringBuilder();
		sb.append("This interface corresponds to the schema ");
		if (Type.class.isInstance(namedAttributeHolder)) {
			sb.append("type");
		} else if (Mixin.class.isInstance(namedAttributeHolder)) {
			sb.append("mixin");
		} else {
			sb.append("element");
		}
		sb.append(" '").append(namedAttributeHolder.getName()).append("' from the schema '").append(namedAttributeHolder.getSchema().getName()).append("' with the namespace '").append(namedAttributeHolder.getSchema().getNamespace()).append("'.\n");
		if (namedAttributeHolder.isVirtual()) {
			sb.append("It is declared virtual and can therefore not be queried.\n");
		}
		String docs = namedAttributeHolder.getAnnotations().get("docs");
		if (docs != null) {
			sb.append(docs).append("\n");
		}
		sb.append("@author ").append(SchemaBeanGeneratorMojo.class.getName());
		return sb.toString();
	}
	
	static void convertToJavaDoc(String documentation, CodeBlock codeBlock) {
		BufferedReader br = new BufferedReader(new StringReader(documentation));
		String line;
		try {
			codeBlock.line("/**");
			while ((line = br.readLine()) != null) {
				// escape closing comments or starting comments in the line with html entities
				codeBlock.line(" * " + line.replaceAll("\\*", "*"));
			}
			codeBlock.line(" */");
		} catch (IOException e) {
			// can't happen, because we are using strings from memory
		}
	}
	
	static String convertToJavaDoc(String documentation, int indent) {
		StringBuilder sb = new StringBuilder();
		BufferedReader br = new BufferedReader(new StringReader(documentation));
		String line;
		try {
			for (int i = 0; i < indent; i++) {
				sb.append('\t');
			}
			sb.append("/**\n");
			while ((line = br.readLine()) != null) {
				for (int i = 0; i < indent; i++) {
					sb.append('\t');
				}
				sb.append(" * ");
				// escape closing comments or starting comments in the line with html entities
				sb.append(line.replaceAll("\\*", "*"));
				sb.append('\n');
			}
			for (int i = 0; i < indent; i++) {
				sb.append('\t');
			}
			sb.append(" */");

		} catch (IOException e) {
			// can't happen, because we are using strings from memory
		}
		return sb.toString();
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy