#44 Creating template for writing MappingMethodReferences, enhancing templating mechanism to pass additional objects to <includeModel/> directive

This commit is contained in:
Gunnar Morling 2013-07-13 13:52:58 +02:00
parent ac9859c38d
commit 6e9abacdd6
6 changed files with 125 additions and 21 deletions

View File

@ -19,15 +19,24 @@
package org.mapstruct.ap.writer; package org.mapstruct.ap.writer;
import java.io.Writer; import java.io.Writer;
import java.util.Map;
import freemarker.ext.beans.BeanModel;
import freemarker.ext.beans.BeansWrapper;
import freemarker.ext.beans.SimpleMapModel;
import freemarker.template.Configuration; import freemarker.template.Configuration;
import freemarker.template.Template; import freemarker.template.Template;
import freemarker.template.TemplateHashModel;
import freemarker.template.TemplateModel;
import freemarker.template.TemplateModelException;
import org.mapstruct.ap.model.ModelElement; import org.mapstruct.ap.model.ModelElement;
import org.mapstruct.ap.model.ModelElement.Context; import org.mapstruct.ap.model.ModelElement.Context;
/** /**
* Delegate for writing given {@link ModelElements} into a {@link Writer} using * Delegate for writing given {@link ModelElement}s into a {@link Writer} using
* FreeMarker templates. * FreeMarker templates. Any parameters passed to the
* {@link ModelIncludeDirective} in addition to element itself can be accessed
* from within the template using the {@code ext} pseudo-element.
* *
* @author Gunnar Morling * @author Gunnar Morling
*/ */
@ -39,8 +48,39 @@ public class FreeMarkerModelElementWriter {
public void write(ModelElement modelElement, String templateName, Context context, Writer writer) throws Exception { public void write(ModelElement modelElement, String templateName, Context context, Writer writer) throws Exception {
Configuration configuration = context.get( Configuration.class ); Configuration configuration = context.get( Configuration.class );
Template template = configuration.getTemplate( templateName ); Template template = configuration.getTemplate( templateName );
template.process( modelElement, writer ); template.process(
new ExternalParamsTemplateModel(
new BeanModel( modelElement, BeansWrapper.getDefaultInstance() ),
new SimpleMapModel( context.get( Map.class ), BeansWrapper.getDefaultInstance() )
),
writer
);
}
private static class ExternalParamsTemplateModel implements TemplateHashModel {
private final BeanModel object;
private final SimpleMapModel extParams;
public ExternalParamsTemplateModel(BeanModel object, SimpleMapModel extParams) {
this.object = object;
this.extParams = extParams;
}
@Override
public TemplateModel get(String key) throws TemplateModelException {
if ( key.equals( "ext" ) ) {
return extParams;
}
else {
return object.get( key );
}
}
@Override
public boolean isEmpty() throws TemplateModelException {
return object.isEmpty() && extParams.isEmpty();
}
} }
} }

View File

@ -19,10 +19,12 @@
package org.mapstruct.ap.writer; package org.mapstruct.ap.writer;
import java.io.IOException; import java.io.IOException;
import java.util.HashMap;
import java.util.Map; import java.util.Map;
import freemarker.core.Environment; import freemarker.core.Environment;
import freemarker.ext.beans.BeanModel; import freemarker.ext.beans.BeanModel;
import freemarker.template.Configuration;
import freemarker.template.TemplateDirectiveBody; import freemarker.template.TemplateDirectiveBody;
import freemarker.template.TemplateDirectiveModel; import freemarker.template.TemplateDirectiveModel;
import freemarker.template.TemplateException; import freemarker.template.TemplateException;
@ -40,10 +42,10 @@ import org.mapstruct.ap.writer.ModelWriter.DefaultModelElementWriterContext;
*/ */
public class ModelIncludeDirective implements TemplateDirectiveModel { public class ModelIncludeDirective implements TemplateDirectiveModel {
private DefaultModelElementWriterContext context; private final Configuration configuration;
public ModelIncludeDirective(DefaultModelElementWriterContext context) { public ModelIncludeDirective(Configuration configuration) {
this.context = context; this.configuration = configuration;
} }
@Override @Override
@ -51,13 +53,8 @@ public class ModelIncludeDirective implements TemplateDirectiveModel {
TemplateDirectiveBody body) TemplateDirectiveBody body)
throws TemplateException, IOException { throws TemplateException, IOException {
Object wrappedObject = ( (BeanModel) params.get( "object" ) ).getWrappedObject(); ModelElement modelElement = getModelElement( params );
DefaultModelElementWriterContext context = createContext( params );
if ( !( wrappedObject instanceof ModelElement ) ) {
throw new IllegalArgumentException( "Given object isn't a ModelElement:" + wrappedObject );
}
ModelElement modelElement = (ModelElement) wrappedObject;
try { try {
modelElement.write( context, env.getOut() ); modelElement.write( context, env.getOut() );
@ -75,4 +72,48 @@ public class ModelIncludeDirective implements TemplateDirectiveModel {
throw new RuntimeException( e ); throw new RuntimeException( e );
} }
} }
@SuppressWarnings("rawtypes")
private ModelElement getModelElement(Map params) {
if ( !params.containsKey( "object" ) ) {
throw new IllegalArgumentException(
"Object to be included must be passed to this directive via the 'object' parameter"
);
}
BeanModel objectModel = (BeanModel) params.get( "object" );
if ( objectModel == null ) {
throw new IllegalArgumentException(
"Object passed to this directive via the 'object' parameter must not be null"
);
}
if ( !( objectModel.getWrappedObject() instanceof ModelElement ) ) {
throw new IllegalArgumentException( "Given object isn't a ModelElement:" + objectModel.getWrappedObject() );
}
return (ModelElement) objectModel.getWrappedObject();
}
/**
* Creates a writer context providing access to the FreeMarker
* {@link Configuration} and a map with any additional parameters passed to
* the directive.
*
* @param params The parameter map passed to this directive.
*
* @return A writer context.
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
private DefaultModelElementWriterContext createContext(Map params) {
Map<String, Object> ext = new HashMap<String, Object>( params );
ext.remove( "object" );
Map<Class<?>, Object> values = new HashMap<Class<?>, Object>();
values.put( Configuration.class, configuration );
values.put( Map.class, ext );
return new DefaultModelElementWriterContext( values );
}
} }

View File

@ -56,7 +56,7 @@ public class ModelWriter {
CONFIGURATION.setObjectWrapper( new DefaultObjectWrapper() ); CONFIGURATION.setObjectWrapper( new DefaultObjectWrapper() );
CONFIGURATION.setSharedVariable( CONFIGURATION.setSharedVariable(
"includeModel", "includeModel",
new ModelIncludeDirective( new DefaultModelElementWriterContext( CONFIGURATION ) ) new ModelIncludeDirective( CONFIGURATION )
); );
} }
@ -64,7 +64,9 @@ public class ModelWriter {
try { try {
BufferedWriter writer = new BufferedWriter( sourceFile.openWriter() ); BufferedWriter writer = new BufferedWriter( sourceFile.openWriter() );
model.write( new DefaultModelElementWriterContext( CONFIGURATION ), writer ); Map<Class<?>, Object> values = new HashMap<Class<?>, Object>();
values.put( Configuration.class, CONFIGURATION );
model.write( new DefaultModelElementWriterContext( values ), writer );
writer.flush(); writer.flush();
writer.close(); writer.close();
@ -85,10 +87,10 @@ public class ModelWriter {
*/ */
static class DefaultModelElementWriterContext implements Context { static class DefaultModelElementWriterContext implements Context {
private Map<Class<?>, Object> values = new HashMap<Class<?>, Object>(); private Map<Class<?>, Object> values;
private DefaultModelElementWriterContext(Configuration configuration) { DefaultModelElementWriterContext(Map<Class<?>, Object> values) {
values.put( Configuration.class, configuration ); this.values = new HashMap<Class<?>, Object>( values );
} }
@Override @Override

View File

@ -42,7 +42,7 @@
</#list> </#list>
</#if> </#if>
<#else> <#else>
${targetType.name?uncap_first}.add( ${elementMappingMethod.name}( ${sourceType.typeParameters[0].name?uncap_first} ) ); ${targetType.name?uncap_first}.add( <@includeModel object=elementMappingMethod input="${sourceType.typeParameters[0].name?uncap_first}"/> );
</#if> </#if>
} }

View File

@ -0,0 +1,21 @@
<#--
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.
-->
<#if declaringMapper??>${declaringMapper.name?uncap_first}.</#if>${name}( ${ext.input} )

View File

@ -20,7 +20,7 @@
--> -->
<#-- a) invoke mapping method --> <#-- a) invoke mapping method -->
<#if mappingMethod??> <#if mappingMethod??>
${targetBeanName}.${targetAccessorName}( <#if mappingMethod.declaringMapper??>${mappingMethod.declaringMapper.name?uncap_first}.</#if>${mappingMethod.name}( ${sourceBeanName}.${sourceAccessorName}() ) ); ${targetBeanName}.${targetAccessorName}( <@includeModel object=mappingMethod input="${sourceBeanName}.${sourceAccessorName}()"/> );
<#-- b) simple conversion --> <#-- b) simple conversion -->
<#elseif conversion??> <#elseif conversion??>
<#if sourceType.primitive == false> <#if sourceType.primitive == false>