diff --git a/spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProtobufAccessorNamingStrategy.java b/spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProtobufAccessorNamingStrategy.java index d821aa7..758d5c7 100755 --- a/spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProtobufAccessorNamingStrategy.java +++ b/spi-impl/src/main/java/no/entur/mapstruct/spi/protobuf/ProtobufAccessorNamingStrategy.java @@ -29,10 +29,7 @@ import java.util.Map; import java.util.Set; -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.ExecutableElement; -import javax.lang.model.element.TypeElement; +import javax.lang.model.element.*; import javax.lang.model.type.DeclaredType; import javax.lang.model.type.TypeMirror; @@ -41,6 +38,8 @@ import org.mapstruct.ap.spi.MapStructProcessingEnvironment; import org.mapstruct.ap.spi.util.IntrospectorUtils; +import static javax.lang.model.type.TypeKind.ARRAY; + /** * @author Arne Seime */ @@ -61,17 +60,21 @@ public class ProtobufAccessorNamingStrategy extends DefaultAccessorNamingStrateg protected static final List INTERNAL_SPECIAL_METHOD_BEGINNINGS = Arrays.asList("remove", "clear", "mutable", "merge", "putAll"); - protected TypeMirror protobufMesageOrBuilderType; + protected TypeMirror protobufMesageOrBuilderType; + protected TypeMirror iterable; @Override public void init(MapStructProcessingEnvironment processingEnvironment) { super.init(processingEnvironment); - TypeElement typeElement = elementUtils.getTypeElement(PROTOBUF_MESSAGE_OR_BUILDER); - if (typeElement != null) { - protobufMesageOrBuilderType = typeElement.asType(); - } - } + TypeElement typeElement = elementUtils.getTypeElement(PROTOBUF_MESSAGE_OR_BUILDER); + if (typeElement != null) { + protobufMesageOrBuilderType = typeElement.asType(); + } + + iterable = typeUtils.erasure(typeUtils.getDeclaredType(elementUtils.getTypeElement(Iterable.class.getName()))); + } + private boolean isSpecialMethod(ExecutableElement method) { if (!isMethodFromProtobufGeneratedClass(method)) { @@ -113,25 +116,26 @@ private boolean isSpecialMethod(ExecutableElement method) { return false; } - @Override - public boolean isGetterMethod(ExecutableElement method) { - String methodName = method.getSimpleName().toString(); + @Override + public boolean isGetterMethod(ExecutableElement method) { + String methodName = method.getSimpleName().toString(); + if (isMethodFromProtobufGeneratedClass(method)) { + if (methodName.endsWith("Builder")) { + return false; + } -// if (methodName.endsWith("Builder")) { -// return false; -// } - - if (methodName.endsWith("OrBuilder")) { - return false; - } + if (methodName.endsWith("OrBuilder")) { + return false; + } - if (methodName.endsWith("OrBuilderList")) { - return false; - } + if (methodName.endsWith("OrBuilderList")) { + return false; + } - if (methodName.endsWith(BUILDER_LIST_SUFFIX)) { - return false; - } + if (methodName.endsWith(BUILDER_LIST_SUFFIX)) { + return false; + } + } if (INTERNAL_METHODS.contains(methodName)) { return false; @@ -158,19 +162,28 @@ private boolean isOneOfCaseSelector(ExecutableElement method) { return false; } - @Override - public boolean isSetterMethod(ExecutableElement method) { - String methodName = method.getSimpleName().toString(); + // Iterable TypeMirror check + + @Override + public boolean isSetterMethod(ExecutableElement method) { + String methodName = method.getSimpleName().toString(); + + if (INTERNAL_METHODS.contains(methodName)) { + return false; + } else { + if (isSpecialMethod(method)) { + return false; + } + List parameters = method.getParameters(); + if (methodName.startsWith("addAll") && parameters.size() > 0) { + VariableElement variableElement = parameters.get(0); + if (isIterable(variableElement)) + return true; // return if method name is addAllXxx and first parameter is Iterable + } + } + return super.isSetterMethod(method); + } - if (INTERNAL_METHODS.contains(methodName)) { - return false; - } else { - if (isSpecialMethod(method)) { - return false; - } - return super.isSetterMethod(method); - } - } @Override protected boolean isFluentSetter(ExecutableElement method) { @@ -232,33 +245,48 @@ public String getElementName(ExecutableElement adderMethod) { return methodName; } - @Override - public String getPropertyName(ExecutableElement getterOrSetterMethod) { - String methodName = getterOrSetterMethod.getSimpleName().toString(); - - boolean isGetMutableMap = isGetMutableMap(getterOrSetterMethod); - boolean isGetUnmodifiableMap = isGetUnmodifiableMap(getterOrSetterMethod); - - if (isGetList(getterOrSetterMethod) || isSetList(getterOrSetterMethod) || isGetMutableMap) { - Element receiver = getterOrSetterMethod.getEnclosingElement(); - if (receiver != null && (receiver.getKind() == ElementKind.CLASS || receiver.getKind() == ElementKind.INTERFACE)) { - TypeElement type = (TypeElement) receiver; - if (isProtobufGeneratedMessage(type)) { - if (isGetMutableMap) { - // 'getMutable...' - return IntrospectorUtils.decapitalize(methodName.substring(10)); - } else if (isGetUnmodifiableMap) { - // 'get...' - return IntrospectorUtils.decapitalize(methodName.substring(3)); - } else { - // 'get...List' - return IntrospectorUtils.decapitalize(methodName.substring(3, methodName.length() - 4)); - } - } - } - } - return super.getPropertyName(getterOrSetterMethod); - } + @Override + public String getPropertyName(ExecutableElement getterOrSetterMethod) { + String methodName = getterOrSetterMethod.getSimpleName().toString(); + + boolean isGetMutableMap = isGetMutableMap(getterOrSetterMethod); + boolean isGetUnmodifiableMap = isGetUnmodifiableMap(getterOrSetterMethod); + Element receiver = getterOrSetterMethod.getEnclosingElement(); + if (receiver != null && (receiver.getKind() == ElementKind.CLASS || receiver.getKind() == ElementKind.INTERFACE)) { + TypeElement type = (TypeElement) receiver; + if (isProtobufGeneratedMessage(type)) { + if (isGetList(getterOrSetterMethod) || isSetList(getterOrSetterMethod) || isGetMutableMap) { + if (isGetMutableMap) { + // 'getMutable...' + return IntrospectorUtils.decapitalize(methodName.substring(10)); + } else if (isGetUnmodifiableMap) { + // 'get...' + return IntrospectorUtils.decapitalize(methodName.substring(3)); + } else if (methodName.startsWith("addAll")) { + // 'addAllXxx' -> 'xxx' + return IntrospectorUtils.decapitalize(methodName.substring(6)); + } else { + // 'get...List' + return IntrospectorUtils.decapitalize(methodName.substring(3, methodName.length() - 4)); + } + } else { + if (typeUtils.isAssignable(getterOrSetterMethod.getReturnType(), protobufMesageOrBuilderType)) { + // addXxxBuilder + if ((methodName.startsWith("add") || methodName.startsWith("get"))) { + if (methodName.endsWith("Builder")) { + return IntrospectorUtils.decapitalize(methodName.substring(3, methodName.length() - 7)); + } else { + return IntrospectorUtils.decapitalize(methodName.substring(3)); + } + + } + + } + } + } + } + return super.getPropertyName(getterOrSetterMethod); + } private boolean isGetList(ExecutableElement element) { return element.getSimpleName().toString().startsWith("get") && isListType(element.getReturnType()); @@ -274,17 +302,29 @@ private boolean isGetMutableMap(ExecutableElement element) { return element.getSimpleName().toString().startsWith("getMutable") && isMapType(element.getReturnType()); } - private boolean isSetList(ExecutableElement element) { - if (element.getSimpleName().toString().startsWith("set") && element.getParameters().size() == 1) { - TypeMirror param = element.getParameters().get(0).asType(); - return isListType(param); - } - return false; - } - - private boolean isListType(TypeMirror t) { - return t.toString().startsWith(List.class.getCanonicalName()) || t.toString().startsWith(PROTOBUF_STRING_LIST_TYPE); - } + private boolean isSetList(ExecutableElement element) { + if (element.getSimpleName().toString().startsWith("addAll") && element.getParameters().size() == 1) { + List parameters = element.getParameters(); + if (parameters.size() > 0) { + VariableElement variableElement = parameters.get(0); + if (isIterable(variableElement)) { + return true; + } + } + } + if (element.getSimpleName().toString().startsWith("set") && element.getParameters().size() == 1) { + TypeMirror param = element.getParameters().get(0).asType(); + return isListType(param); + } + return false; + } + + private boolean isListType(TypeMirror t) { + if (t.getKind() == ARRAY) { + return true; + } + return t.toString().startsWith(List.class.getCanonicalName()) || t.toString().startsWith(PROTOBUF_STRING_LIST_TYPE); + } private boolean isMapType(TypeMirror t) { return t.toString().startsWith(Map.class.getCanonicalName()); @@ -325,4 +365,12 @@ private boolean isMethodFromProtobufBuilder(ExecutableElement method) { TypeElement cls = (TypeElement) method.getEnclosingElement(); return cls.getSuperclass().toString().startsWith(PROTOBUF_BUILDER); } + + private boolean isIterable(VariableElement variableElement) { + TypeMirror typeMirror = variableElement.asType(); + if (typeUtils.isAssignable(typeMirror, iterable)) { + return true; + } + return false; + } } diff --git a/usage/src/main/java/no/entur/mapstruct/spi/protobuf/User.java b/usage/src/main/java/no/entur/mapstruct/spi/protobuf/User.java index 5a07a45..8da1596 100755 --- a/usage/src/main/java/no/entur/mapstruct/spi/protobuf/User.java +++ b/usage/src/main/java/no/entur/mapstruct/spi/protobuf/User.java @@ -76,6 +76,7 @@ public class User { List rv14; List rv15; List rv16; + Status[] rv17; MultiNumber multiNumber; List repMultiNumbers; private String id; @@ -444,4 +445,12 @@ public List getUsers() { public void setUsers(List users) { this.users = users; } + + public Status[] getRv17() { + return rv17; + } + + public void setRv17(Status[] rv17) { + this.rv17 = rv17; + } } diff --git a/usage/src/main/proto/User.proto b/usage/src/main/proto/User.proto index 5c1fa10..02100e1 100755 --- a/usage/src/main/proto/User.proto +++ b/usage/src/main/proto/User.proto @@ -75,6 +75,7 @@ message UserDTO { repeated bytes rv15 = 114; repeated EnumStatus rv16 = 115; + repeated EnumStatus rv17 = 116; // repeated google.protobuf.Any rv17 = 116; repeated MultiNumberDTO rep_multi_numbers = 120; diff --git a/usage/src/test/java/no/entur/mapstruct/example/MappingTest.java b/usage/src/test/java/no/entur/mapstruct/example/MappingTest.java index 39396ff..7cb646a 100755 --- a/usage/src/test/java/no/entur/mapstruct/example/MappingTest.java +++ b/usage/src/test/java/no/entur/mapstruct/example/MappingTest.java @@ -152,6 +152,7 @@ private User generateUser() { user.setRv14(Arrays.asList("some string")); user.setRv15(Arrays.asList(ByteString.copyFromUtf8("some byte string"))); user.setRv16(Arrays.asList(Status.STARTED)); + user.setRv17(new Status[]{Status.STARTED, Status.STOPPED}); MultiNumber mm = new MultiNumber(); mm.setNumber(1); @@ -242,6 +243,10 @@ private void assertUser(User orig, User back) { assertEquals(orig.getRv16().size(), back.getRv16().size()); assertEquals(orig.getRv16().get(0), back.getRv16().get(0)); + assertEquals(orig.getRv17().length, back.getRv17().length); + assertEquals(orig.getRv17()[0], back.getRv17()[0]); + assertEquals(orig.getRv17()[1], back.getRv17()[1]); + assertNotNull(back.getPoliceDepartment()); assertEquals(back.getPoliceDepartment().getName(), "POLICE"); // WILL FAIL: Mapstruct cannot handle presence checker on oneOfs : assertNull(back.getFireDepartment());