diff --git a/core/src/main/java/org/mapstruct/MapMapping.java b/core/src/main/java/org/mapstruct/MapMapping.java new file mode 100644 index 000000000..66cc90634 --- /dev/null +++ b/core/src/main/java/org/mapstruct/MapMapping.java @@ -0,0 +1,53 @@ +/** + * 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; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import java.text.SimpleDateFormat; +import java.util.Date; + +/** + * Configures the mapping between two map types, e.g. {@code Map} and {@code Map}. + * + * @author Gunnar Morling + */ +@Target(ElementType.METHOD) +@Retention(RetentionPolicy.SOURCE) +public @interface MapMapping { + + /** + * A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with key type + * {@code String} to an map with key type {@link Date} or vice-versa. Will be ignored for all other key types. + * + * @return A date format string as processable by {@link SimpleDateFormat}. + */ + String keyDateFormat() default ""; + + /** + * A format string as processable by {@link SimpleDateFormat} if the annotated method maps from a map with value + * type {@code String} to an map with value type {@link Date} or vice-versa. Will be ignored for all other value + * types. + * + * @return A date format string as processable by {@link SimpleDateFormat}. + */ + String valueDateFormat() default ""; +} diff --git a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java index 810f3e55e..0ad4cec92 100644 --- a/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/MappingProcessor.java @@ -40,9 +40,10 @@ import javax.lang.model.util.ElementKindVisitor6; import net.java.dev.hickory.prism.GeneratePrism; import net.java.dev.hickory.prism.GeneratePrisms; import org.mapstruct.IterableMapping; +import org.mapstruct.MapMapping; +import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.Mappings; -import org.mapstruct.ap.model.Mapper; import org.mapstruct.ap.model.Options; import org.mapstruct.ap.model.ReportingPolicy; import org.mapstruct.ap.processor.DefaultModelElementProcessorContext; @@ -71,10 +72,11 @@ import org.mapstruct.ap.processor.ModelElementProcessor.ProcessorContext; */ @SupportedAnnotationTypes("org.mapstruct.Mapper") @GeneratePrisms({ - @GeneratePrism(value = org.mapstruct.Mapper.class, publicAccess = true), + @GeneratePrism(value = Mapper.class, publicAccess = true), @GeneratePrism(value = Mapping.class, publicAccess = true), @GeneratePrism(value = Mappings.class, publicAccess = true), - @GeneratePrism(value = IterableMapping.class, publicAccess = true) + @GeneratePrism(value = IterableMapping.class, publicAccess = true), + @GeneratePrism(value = MapMapping.class, publicAccess = true) }) @SupportedOptions({ MappingProcessor.SUPPRESS_GENERATOR_TIMESTAMP, diff --git a/processor/src/main/java/org/mapstruct/ap/model/Type.java b/processor/src/main/java/org/mapstruct/ap/model/Type.java index 655d329b1..dd8f8ea23 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/Type.java +++ b/processor/src/main/java/org/mapstruct/ap/model/Type.java @@ -30,6 +30,8 @@ import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; +import org.mapstruct.ap.util.Strings; + /** * Represents the type of a bean property, parameter etc. * @@ -244,4 +246,14 @@ public class Type extends AbstractModelElement implements Comparable { public int compareTo(Type o) { return getFullyQualifiedName().compareTo( o.getFullyQualifiedName() ); } + + @Override + public String toString() { + if ( !typeParameters.isEmpty() ) { + return name + "<" + Strings.join( typeParameters, ", " ) + ">"; + } + else { + return name; + } + } } diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/MapMapping.java b/processor/src/main/java/org/mapstruct/ap/model/source/MapMapping.java new file mode 100644 index 000000000..506b5e12e --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/model/source/MapMapping.java @@ -0,0 +1,65 @@ +/** + * 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.source; + +import javax.lang.model.element.AnnotationMirror; + +import org.mapstruct.ap.MapMappingPrism; + +/** + * Represents a map mapping as configured via {@code @MapMapping}. + * + * @author Gunnar Morling + */ +public class MapMapping { + + private final String keyFormat; + private final String valueFormat; + private final AnnotationMirror mirror; + + public static MapMapping fromPrism(MapMappingPrism mapMapping) { + if ( mapMapping == null ) { + return null; + } + + return new MapMapping( + mapMapping.keyDateFormat(), + mapMapping.valueDateFormat(), + mapMapping.mirror + ); + } + + private MapMapping(String keyFormat, String valueFormat, AnnotationMirror mirror) { + this.keyFormat = keyFormat; + this.valueFormat = valueFormat; + this.mirror = mirror; + } + + public String getKeyFormat() { + return keyFormat; + } + + public String getValueFormat() { + return valueFormat; + } + + public AnnotationMirror getMirror() { + return mirror; + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java index c4c2a69bd..3887b71d2 100644 --- a/processor/src/main/java/org/mapstruct/ap/model/source/Method.java +++ b/processor/src/main/java/org/mapstruct/ap/model/source/Method.java @@ -39,11 +39,12 @@ public class Method { private final Type targetType; private Map mappings; private IterableMapping iterableMapping; + private MapMapping mapMapping; public static Method forMethodRequiringImplementation(ExecutableElement executable, String parameterName, Type sourceType, Type targetType, Map mappings, - IterableMapping iterableMapping) { + IterableMapping iterableMapping, MapMapping mapMapping) { return new Method( null, @@ -52,7 +53,8 @@ public class Method { sourceType, targetType, mappings, - iterableMapping + iterableMapping, + mapMapping ); } @@ -66,12 +68,14 @@ public class Method { sourceType, targetType, Collections.emptyMap(), + null, null ); } private Method(Type declaringMapper, ExecutableElement executable, String parameterName, Type sourceType, - Type targetType, Map mappings, IterableMapping iterableMapping) { + Type targetType, Map mappings, IterableMapping iterableMapping, + MapMapping mapMapping) { this.declaringMapper = declaringMapper; this.executable = executable; this.parameterName = parameterName; @@ -79,6 +83,7 @@ public class Method { this.targetType = targetType; this.mappings = mappings; this.iterableMapping = iterableMapping; + this.mapMapping = mapMapping; } /** @@ -128,6 +133,14 @@ public class Method { this.iterableMapping = iterableMapping; } + public MapMapping getMapMapping() { + return mapMapping; + } + + public void setMapMapping(MapMapping mapMapping) { + this.mapMapping = mapMapping; + } + public boolean reverses(Method method) { return equals( sourceType, method.getTargetType() ) && diff --git a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java index eaf6033a3..093df31a8 100644 --- a/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java +++ b/processor/src/main/java/org/mapstruct/ap/processor/MapperCreationProcessor.java @@ -166,6 +166,10 @@ public class MapperCreationProcessor implements ModelElementProcessor values = new HashMap(); @@ -57,14 +50,14 @@ public class MapMappingTest extends MapperTestBase { assertThat( target ).isNotNull(); assertThat( target ).hasSize( 2 ); - assertThat( target ).includes( entry( "42", "01.01.80 00:00" ), entry( "121", "20.07.13 00:00" ) ); + assertThat( target ).includes( entry( "42", "01.01.1980" ), entry( "121", "20.07.2013" ) ); } @Test public void shouldCreateReverseMapMethodImplementation() { Map values = new HashMap(); - values.put( "42", "01.01.80 00:00" ); - values.put( "121", "20.07.13 00:00" ); + values.put( "42", "01.01.1980" ); + values.put( "121", "20.07.2013" ); Map target = SourceTargetMapper.INSTANCE.stringStringMapToLongDateMap( values ); diff --git a/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java b/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java index c7ec62199..067803ea1 100644 --- a/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java +++ b/processor/src/test/java/org/mapstruct/ap/test/collection/map/SourceTargetMapper.java @@ -21,6 +21,7 @@ package org.mapstruct.ap.test.collection.map; import java.util.Date; import java.util.Map; +import org.mapstruct.MapMapping; import org.mapstruct.Mapper; import org.mapstruct.Mappers; @@ -29,6 +30,7 @@ public interface SourceTargetMapper { SourceTargetMapper INSTANCE = Mappers.getMapper( SourceTargetMapper.class ); + @MapMapping( valueDateFormat = "dd.MM.yyyy" ) Map longDateMapToStringStringMap(Map source); Map stringStringMapToLongDateMap(Map source);