#32 Preparing aggregated model to allow nodes to write out themselves using specific templates

This commit is contained in:
Gunnar Morling 2013-06-16 23:11:30 +02:00
parent 522c9e851a
commit 781f698a8a
8 changed files with 254 additions and 14 deletions

View File

@ -130,8 +130,7 @@ public class MapperGenerationVisitor extends ElementKindVisitor6<Void, Void> {
throw new RuntimeException( e );
}
ModelWriter modelWriter = new ModelWriter( "mapper-implementation.ftl" );
modelWriter.writeModel( sourceFile, model );
new ModelWriter().writeModel( sourceFile, model );
}
private Mapper retrieveModel(TypeElement element) {

View File

@ -0,0 +1,39 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.model;
import java.io.Writer;
import org.mapstruct.ap.writer.FreeMarkerModelElementWriter;
/**
* Default implementation of {@link ModelElement} which writes model elements
* using FreeMarker templates. By default, the fully-qualified class name of the
* given model element type, appended with the extension {@code *.ftl} is used
* as template file name.
*
* @author Gunnar Morling
*/
public class AbstractModelElement implements ModelElement {
@Override
public void write(Context context, Writer writer) throws Exception {
new FreeMarkerModelElementWriter().write( this, context, writer );
}
}

View File

@ -24,7 +24,7 @@ import java.util.SortedSet;
import java.util.TreeSet;
import javax.annotation.Generated;
public class Mapper {
public class Mapper extends AbstractModelElement {
private final String packageName;
private final String interfaceName;

View File

@ -0,0 +1,57 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.model;
import java.io.Writer;
/**
* A model element with the ability to write itself into a given {@link Writer}.
*
* @author Gunnar Morling
*/
public interface ModelElement {
/**
* Passed to {@link ModelElement}, providing access to additional data
* specific to a given implementation of the model serialization mechanism.
*
* @author Gunnar Morling
*/
interface Context {
/**
* Retrieves the object with the given type from this context.
*
* @param type The type of the object to retrieve from this context.
*
* @return The object with the given type from this context.
*/
<T> T get(Class<T> type);
}
/**
* Writes this model element to the given writer.
*
* @param context Provides additional data specific to the used implementation
* of the model serialization mechanism.
* @param writer The writer to write this model to. Must not be closed by
* implementations.
*/
void write(Context context, Writer writer) throws Exception;
}

View File

@ -0,0 +1,46 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.writer;
import java.io.Writer;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.mapstruct.ap.model.ModelElement;
import org.mapstruct.ap.model.ModelElement.Context;
/**
* Delegate for writing given {@link ModelElements} into a {@link Writer} using
* FreeMarker templates.
*
* @author Gunnar Morling
*/
public class FreeMarkerModelElementWriter {
public void write(ModelElement modelElement, Context context, Writer writer) throws Exception {
write( modelElement, modelElement.getClass().getName() + ".ftl", context, writer );
}
public void write(ModelElement modelElement, String templateName, Context context, Writer writer) throws Exception {
Configuration configuration = context.get( Configuration.class );
Template template = configuration.getTemplate( templateName );
template.process( modelElement, writer );
}
}

View File

@ -0,0 +1,78 @@
/**
* Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/)
* and/or other contributors as indicated by the @authors tag. See the
* copyright.txt file in the distribution for a full listing of all
* contributors.
*
* 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 org.mapstruct.ap.writer;
import java.io.IOException;
import java.util.Map;
import freemarker.core.Environment;
import freemarker.ext.beans.BeanModel;
import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException;
import freemarker.template.TemplateModel;
import org.mapstruct.ap.model.ModelElement;
import org.mapstruct.ap.writer.ModelWriter.DefaultModelElementWriterContext;
/**
* A {@link TemplateDirectiveModel} which allows to recursively write a graph of
* {@link ModelElement}s, with each element using its own template. Elements are
* imported into the parent template by using this directive like so:
* {@code <@includeModel object=myProperty/>}.
*
* @author Gunnar Morling
*/
public class ModelIncludeDirective implements TemplateDirectiveModel {
private DefaultModelElementWriterContext context;
public ModelIncludeDirective(DefaultModelElementWriterContext context) {
this.context = context;
}
@Override
public void execute(Environment env, @SuppressWarnings("rawtypes") Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body)
throws TemplateException, IOException {
Object wrappedObject = ( (BeanModel) params.get( "object" ) ).getWrappedObject();
if ( !( wrappedObject instanceof ModelElement ) ) {
throw new IllegalArgumentException( "Given object isn't a ModelElement:" + wrappedObject );
}
ModelElement modelElement = (ModelElement) wrappedObject;
try {
modelElement.write( context, env.getOut() );
}
catch ( TemplateException te ) {
throw te;
}
catch ( IOException ioe ) {
throw ioe;
}
catch ( RuntimeException re ) {
throw re;
}
catch ( Exception e ) {
throw new RuntimeException( e );
}
}
}

View File

@ -19,11 +19,14 @@
package org.mapstruct.ap.writer;
import java.io.BufferedWriter;
import java.util.HashMap;
import java.util.Map;
import javax.tools.JavaFileObject;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;
import org.mapstruct.ap.model.ModelElement;
import org.mapstruct.ap.model.ModelElement.Context;
/**
* Writes Java source files based on given mapper models, using a FreeMarker
@ -39,25 +42,22 @@ public class ModelWriter {
*/
private static final Configuration CONFIGURATION;
private final String templateName;
static {
CONFIGURATION = new Configuration();
CONFIGURATION.setClassForTemplateLoading( ModelWriter.class, "/" );
CONFIGURATION.setObjectWrapper( new DefaultObjectWrapper() );
CONFIGURATION.setSharedVariable(
"includeModel",
new ModelIncludeDirective( new DefaultModelElementWriterContext( CONFIGURATION ) )
);
}
public ModelWriter(String templateName) {
this.templateName = templateName;
}
public void writeModel(JavaFileObject sourceFile, Object model) {
public void writeModel(JavaFileObject sourceFile, ModelElement model) {
try {
BufferedWriter writer = new BufferedWriter( sourceFile.openWriter() );
Template template = CONFIGURATION.getTemplate( templateName );
template.process( model, writer );
model.write( new DefaultModelElementWriterContext( CONFIGURATION ), writer );
writer.flush();
writer.close();
}
@ -68,4 +68,25 @@ public class ModelWriter {
throw new RuntimeException( e );
}
}
/**
* {@link Context} implementation which provides access to the current
* FreeMarker {@link Configuration}.
*
* @author Gunnar Morling
*/
static class DefaultModelElementWriterContext implements Context {
private Map<Class<?>, Object> values = new HashMap<Class<?>, Object>();
private DefaultModelElementWriterContext(Configuration configuration) {
values.put( Configuration.class, configuration );
}
@Override
@SuppressWarnings("unchecked")
public <T> T get(Class<T> type) {
return (T) values.get( type );
}
}
}