diff --git a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java index e1d6132b2..bfe2a1f60 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/common/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/model/common/Type.java @@ -523,6 +523,40 @@ public class Type extends ModelElement implements Comparable { return true; } + /** + * A valid Java expression most suitable for representing null - useful for dealing with primitives from FTL. + */ + public String getNull() { + if ( !isPrimitive() || isArrayType() ) { + return "null"; + } + if ( "boolean".equals( getName() ) ) { + return "false"; + } + if ( "byte".equals( getName() ) ) { + return "0"; + } + if ( "char".equals( getName() ) ) { + return "'\\u0000'"; + } + if ( "double".equals( getName() ) ) { + return "0.0"; + } + if ( "float".equals( getName() ) ) { + return "0.0f"; + } + if ( "int".equals( getName() ) ) { + return "0"; + } + if ( "long".equals( getName() ) ) { + return "0L"; + } + if ( "short".equals( getName() ) ) { + return "0"; + } + throw new UnsupportedOperationException( getName() ); + } + @Override public int hashCode() { final int prime = 31; diff --git a/processor/src/main/resources/org.mapstruct.ap.model.NestedPropertyMappingMethod.ftl b/processor/src/main/resources/org.mapstruct.ap.model.NestedPropertyMappingMethod.ftl index 3f479a9a0..a04dee806 100644 --- a/processor/src/main/resources/org.mapstruct.ap.model.NestedPropertyMappingMethod.ftl +++ b/processor/src/main/resources/org.mapstruct.ap.model.NestedPropertyMappingMethod.ftl @@ -21,13 +21,15 @@ <#lt>private <@includeModel object=returnType/> ${name}(<#list parameters as param><@includeModel object=param/><#if param_has_next>, ) { if ( ${sourceParameter.name} == null ) { - return null; + return ${returnType.null}; } <#list propertyEntries as entry> <@includeModel object=entry.type/> ${entry.name} = <#if entry_index == 0>${sourceParameter.name}.${entry.accessor}<#else>${propertyEntries[entry_index-1].name}.${entry.accessor}; + <#if !entry.type.primitive> if ( ${entry.name} == null ) { - return null; + return ${returnType.null}; } + <#if !entry_has_next> return ${entry.name}; diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleMapper.java b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleMapper.java new file mode 100644 index 000000000..18ec63c5e --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleMapper.java @@ -0,0 +1,45 @@ +/** + * Copyright 2012-2014 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.test.nestedproperties.simple; + +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Mappings; +import org.mapstruct.ap.test.nestedproperties.simple.source.SourceRoot; +import org.mapstruct.ap.test.nestedproperties.simple.target.TargetObject; +import org.mapstruct.factory.Mappers; + +@Mapper +public interface SimpleMapper { + + SimpleMapper MAPPER = Mappers.getMapper( SimpleMapper.class ); + + @Mappings( { @Mapping( target = "longValue", source = "props.longValue" ), + @Mapping( target = "intValue", source = "props.intValue" ), + @Mapping( target = "doubleValue", source = "props.doubleValue" ), + @Mapping( target = "floatValue", source = "props.floatValue" ), + @Mapping( target = "shortValue", source = "props.shortValue" ), + @Mapping( target = "charValue", source = "props.charValue" ), + @Mapping( target = "byteValue", source = "props.byteValue" ), + @Mapping( target = "booleanValue", source = "props.booleanValue" ), + @Mapping( target = "byteArray", source = "props.byteArray" ), + @Mapping( target = "stringValue", source = "props.stringValue" ) } ) + TargetObject toTargetObject(SourceRoot sourceRoot); + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleNestedPropertiesTest.java b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleNestedPropertiesTest.java new file mode 100644 index 000000000..104d9e5cf --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/SimpleNestedPropertiesTest.java @@ -0,0 +1,104 @@ +/** + * Copyright 2012-2014 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.test.nestedproperties.simple; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mapstruct.ap.test.nestedproperties.simple.source.SourceProps; +import org.mapstruct.ap.test.nestedproperties.simple.source.SourceRoot; +import org.mapstruct.ap.test.nestedproperties.simple.target.TargetObject; +import org.mapstruct.ap.testutil.IssueKey; +import org.mapstruct.ap.testutil.WithClasses; +import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner; + +/** + * @author Sebastian Hasait + */ +@WithClasses( { SourceRoot.class, SourceProps.class, TargetObject.class } ) +@IssueKey( "407" ) +@RunWith( AnnotationProcessorTestRunner.class ) +public class SimpleNestedPropertiesTest { + + @Test + @WithClasses( { SimpleMapper.class } ) + public void testNull() { + TargetObject targetObject = SimpleMapper.MAPPER.toTargetObject( null ); + + assertNull( targetObject ); + } + + @Test + @WithClasses( { SimpleMapper.class } ) + public void testViaNull() { + SourceRoot sourceRoot = new SourceRoot(); + // sourceRoot.getProps() is null + + TargetObject targetObject = SimpleMapper.MAPPER.toTargetObject( sourceRoot ); + + assertEquals( 0L, targetObject.getLongValue() ); + assertEquals( 0, targetObject.getIntValue() ); + assertEquals( 0.0, targetObject.getDoubleValue(), 0.01 ); + assertEquals( 0.0f, targetObject.getFloatValue(), 0.01f ); + assertEquals( 0, targetObject.getShortValue() ); + assertEquals( 0, targetObject.getCharValue() ); + assertEquals( 0, targetObject.getByteValue() ); + assertFalse( targetObject.isBooleanValue() ); + assertNull( targetObject.getByteArray() ); + assertNull( targetObject.getStringValue() ); + } + + @Test + @WithClasses( { SimpleMapper.class } ) + public void testFilled() { + SourceRoot sourceRoot = new SourceRoot(); + SourceProps sourceProps = new SourceProps(); + sourceRoot.setProps( sourceProps ); + sourceProps.setLongValue( Long.MAX_VALUE ); + sourceProps.setIntValue( Integer.MAX_VALUE ); + sourceProps.setDoubleValue( Double.MAX_VALUE ); + sourceProps.setFloatValue( Float.MAX_VALUE ); + sourceProps.setShortValue( Short.MAX_VALUE ); + sourceProps.setCharValue( Character.MAX_VALUE ); + sourceProps.setByteValue( Byte.MAX_VALUE ); + sourceProps.setBooleanValue( true ); + String stringValue = "lorem ipsum"; + sourceProps.setByteArray( stringValue.getBytes() ); + sourceProps.setStringValue( stringValue ); + + TargetObject targetObject = SimpleMapper.MAPPER.toTargetObject( sourceRoot ); + + assertEquals( Long.MAX_VALUE, targetObject.getLongValue() ); + assertEquals( Integer.MAX_VALUE, targetObject.getIntValue() ); + assertEquals( Double.MAX_VALUE, targetObject.getDoubleValue(), 0.01 ); + assertEquals( Float.MAX_VALUE, targetObject.getFloatValue(), 0.01f ); + assertEquals( Short.MAX_VALUE, targetObject.getShortValue() ); + assertEquals( Character.MAX_VALUE, targetObject.getCharValue() ); + assertEquals( Byte.MAX_VALUE, targetObject.getByteValue() ); + assertTrue( targetObject.isBooleanValue() ); + assertArrayEquals( stringValue.getBytes(), targetObject.getByteArray() ); + assertEquals( stringValue, targetObject.getStringValue() ); + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/source/SourceProps.java b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/source/SourceProps.java new file mode 100644 index 000000000..33067ccfa --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/source/SourceProps.java @@ -0,0 +1,116 @@ +/** + * Copyright 2012-2014 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.test.nestedproperties.simple.source; + +public class SourceProps { + + private long longValue; + private int intValue; + private double doubleValue; + private float floatValue; + private short shortValue; + private char charValue; + private byte byteValue; + private boolean booleanValue; + + private byte[] byteArray; + + private String stringValue; + + public long getLongValue() { + return longValue; + } + + public void setLongValue(long longValue) { + this.longValue = longValue; + } + + public int getIntValue() { + return intValue; + } + + public void setIntValue(int intValue) { + this.intValue = intValue; + } + + public double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(double doubleValue) { + this.doubleValue = doubleValue; + } + + public float getFloatValue() { + return floatValue; + } + + public void setFloatValue(float floatValue) { + this.floatValue = floatValue; + } + + public short getShortValue() { + return shortValue; + } + + public void setShortValue(short shortValue) { + this.shortValue = shortValue; + } + + public char getCharValue() { + return charValue; + } + + public void setCharValue(char charValue) { + this.charValue = charValue; + } + + public byte getByteValue() { + return byteValue; + } + + public void setByteValue(byte byteValue) { + this.byteValue = byteValue; + } + + public byte[] getByteArray() { + return byteArray; + } + + public void setByteArray(byte[] byteArray) { + this.byteArray = byteArray; + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + public void setBooleanValue(boolean booleanValue) { + this.booleanValue = booleanValue; + } + + public boolean isBooleanValue() { + return booleanValue; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/source/SourceRoot.java b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/source/SourceRoot.java new file mode 100644 index 000000000..376d46606 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/source/SourceRoot.java @@ -0,0 +1,33 @@ +/** + * Copyright 2012-2014 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.test.nestedproperties.simple.source; + +public class SourceRoot { + + private SourceProps props; + + public void setProps(SourceProps props) { + this.props = props; + } + + public SourceProps getProps() { + return props; + } + +} diff --git a/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/target/TargetObject.java b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/target/TargetObject.java new file mode 100644 index 000000000..6938b8522 --- /dev/null +++ b/processor/src/test/java/org/mapstruct/ap/test/nestedproperties/simple/target/TargetObject.java @@ -0,0 +1,116 @@ +/** + * Copyright 2012-2014 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.test.nestedproperties.simple.target; + +public class TargetObject { + + private long longValue; + private int intValue; + private double doubleValue; + private float floatValue; + private short shortValue; + private char charValue; + private byte byteValue; + private boolean booleanValue; + + private byte[] byteArray; + + private String stringValue; + + public long getLongValue() { + return longValue; + } + + public void setLongValue(long longValue) { + this.longValue = longValue; + } + + public int getIntValue() { + return intValue; + } + + public void setIntValue(int intValue) { + this.intValue = intValue; + } + + public double getDoubleValue() { + return doubleValue; + } + + public void setDoubleValue(double doubleValue) { + this.doubleValue = doubleValue; + } + + public float getFloatValue() { + return floatValue; + } + + public void setFloatValue(float floatValue) { + this.floatValue = floatValue; + } + + public short getShortValue() { + return shortValue; + } + + public void setShortValue(short shortValue) { + this.shortValue = shortValue; + } + + public char getCharValue() { + return charValue; + } + + public void setCharValue(char charValue) { + this.charValue = charValue; + } + + public byte getByteValue() { + return byteValue; + } + + public void setByteValue(byte byteValue) { + this.byteValue = byteValue; + } + + public byte[] getByteArray() { + return byteArray; + } + + public void setByteArray(byte[] byteArray) { + this.byteArray = byteArray; + } + + public String getStringValue() { + return stringValue; + } + + public void setStringValue(String stringValue) { + this.stringValue = stringValue; + } + + public void setBooleanValue(boolean booleanValue) { + this.booleanValue = booleanValue; + } + + public boolean isBooleanValue() { + return booleanValue; + } + +}