#365 Making Services factory agnostic of specific services

This commit is contained in:
Gunnar Morling 2015-05-16 19:56:52 +02:00 committed by Andreas Gudian
parent 3ba4ded58c
commit 77298c750e
5 changed files with 67 additions and 38 deletions

View File

@ -20,12 +20,10 @@ package org.mapstruct.ap.model.assignment;
import java.util.HashSet;
import java.util.Set;
import javax.lang.model.element.ExecutableElement;
import org.mapstruct.ap.model.common.Type;
import org.mapstruct.ap.services.Services;
import org.mapstruct.ap.spi.AccessorNamingStrategy;
import org.mapstruct.ap.util.Executables;
/**
* This wrapper handles the situation were an assignment is done via the setter.
@ -45,15 +43,11 @@ public class SetterWrapperForCollectionsAndMaps extends AssignmentWrapper {
private final Assignment newCollectionOrMapAssignment;
public SetterWrapperForCollectionsAndMaps(Assignment decoratedAssignment,
ExecutableElement targetSetter,
Assignment newCollectionOrMapAssignment) {
ExecutableElement targetSetter,
Assignment newCollectionOrMapAssignment) {
super( decoratedAssignment );
AccessorNamingStrategy accessorNamingStrategy = Services.getAccessorNamingStrategy();
this.targetGetterName = accessorNamingStrategy.getCollectionGetterName(
accessorNamingStrategy.getPropertyName( targetSetter )
);
this.targetGetterName = Executables.getCollectionGetterName( targetSetter );
this.newCollectionOrMapAssignment = newCollectionOrMapAssignment;
}

View File

@ -16,10 +16,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.mapstruct.ap.services;
package org.mapstruct.ap.naming;
import java.beans.Introspector;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.DeclaredType;
@ -36,7 +35,7 @@ import org.mapstruct.ap.spi.MethodType;
*
* @author Christian Schuster
*/
class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
public class DefaultAccessorNamingStrategy implements AccessorNamingStrategy {
@Override
public MethodType getMethodType(ExecutableElement method) {

View File

@ -0,0 +1,24 @@
/**
* Copyright 2012-2015 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.
*/
/**
* <p>
* SPI (service provider interface) contracts to be implemented for advanced use cases.
* </p>
*/
package org.mapstruct.ap.naming;

View File

@ -19,8 +19,8 @@
package org.mapstruct.ap.services;
import java.util.ServiceLoader;
import org.mapstruct.ap.spi.AccessorNamingStrategy;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
/**
* A simple locator for SPI implementations.
@ -29,32 +29,35 @@ import org.mapstruct.ap.spi.AccessorNamingStrategy;
*/
public class Services {
private static final AccessorNamingStrategy ACCESSOR_NAMING_STRATEGY = findAccessorNamingStrategy();
private static final ConcurrentMap<Class<?>, Object> SERVICES = new ConcurrentHashMap<Class<?>, Object>();
private Services() {
}
/**
* Obtain an implementation of {@link AccessorNamingStrategy}. If no specialized implementation is found using
* {@link ServiceLoader}, a JavaBeans-compliant default implementation is returned. The result is cached across
* invocations.
*
* @return The implementation of {@link AccessorNamingStrategy}.
* @throws IllegalStateException If more than one implementation is found by
* {@link ServiceLoader#load(Class, ClassLoader)}.
*/
public static AccessorNamingStrategy getAccessorNamingStrategy() {
return ACCESSOR_NAMING_STRATEGY;
}
public static <T> T get(Class<T> serviceType, T defaultValue) {
@SuppressWarnings("unchecked")
T service = (T) SERVICES.get( serviceType );
private static AccessorNamingStrategy findAccessorNamingStrategy() {
AccessorNamingStrategy defaultImpl = new DefaultAccessorNamingStrategy();
AccessorNamingStrategy impl = find( AccessorNamingStrategy.class );
if ( impl == null ) {
impl = defaultImpl;
if ( service == null ) {
service = loadAndCache( serviceType, defaultValue );
}
return impl;
return service;
}
private static <T> T loadAndCache(Class<T> serviceType, T defaultValue) {
T service = find( serviceType );
if ( service == null ) {
service = defaultValue;
}
@SuppressWarnings("unchecked")
T cached = (T) SERVICES.putIfAbsent( serviceType, service );
if ( cached != null ) {
service = (T) cached;
}
return service;
}
private static <T> T find(Class<T> spi) {
@ -68,7 +71,8 @@ public class Services {
throw new IllegalStateException(
"Multiple implementations have been found for the service provider interface "
+ spi.getCanonicalName() + ": " + matchingImplementation.getClass().getCanonicalName() + ", "
+ implementation.getClass().getCanonicalName() + "." );
+ implementation.getClass().getCanonicalName() + "."
);
}
}

View File

@ -20,7 +20,6 @@ package org.mapstruct.ap.util;
import java.util.ArrayList;
import java.util.List;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
@ -29,6 +28,7 @@ import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.lang.model.util.Elements;
import org.mapstruct.ap.naming.DefaultAccessorNamingStrategy;
import org.mapstruct.ap.services.Services;
import org.mapstruct.ap.spi.AccessorNamingStrategy;
import org.mapstruct.ap.spi.MethodType;
@ -43,7 +43,10 @@ import static org.mapstruct.ap.util.SpecificCompilerWorkarounds.replaceTypeEleme
*/
public class Executables {
private static AccessorNamingStrategy accessorNamingStrategy = Services.getAccessorNamingStrategy();
private static AccessorNamingStrategy accessorNamingStrategy = Services.get(
AccessorNamingStrategy.class,
new DefaultAccessorNamingStrategy()
);
private Executables() {
}
@ -51,7 +54,7 @@ public class Executables {
public static boolean isGetterMethod(ExecutableElement method) {
return isPublic( method ) &&
method.getParameters().isEmpty() &&
Services.getAccessorNamingStrategy().getMethodType( method ) == MethodType.GETTER;
accessorNamingStrategy.getMethodType( method ) == MethodType.GETTER;
}
public static boolean isSetterMethod(ExecutableElement method) {
@ -83,6 +86,11 @@ public class Executables {
return accessorNamingStrategy.getElementName( adderMethod );
}
public static String getCollectionGetterName(ExecutableElement targetSetter) {
String propertyName = accessorNamingStrategy.getPropertyName( targetSetter );
return accessorNamingStrategy.getCollectionGetterName( propertyName );
}
/**
* @param mirror the type mirror
*