/** * Copyright 2012-2017 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.internal.processor; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.jar.Manifest; import javax.annotation.processing.ProcessingEnvironment; import org.mapstruct.ap.internal.version.VersionInformation; /** * Provides information about the processor version and the processor context implementation version. * <p> * Separated into an interface and this implementation to avoid cyclic dependencies between the processor package and * the model package. * * @author Andreas Gudian */ public class DefaultVersionInformation implements VersionInformation { private static final String JAVAC_PE_CLASS = "com.sun.tools.javac.processing.JavacProcessingEnvironment"; private static final String COMPILER_NAME_JAVAC = "javac"; private static final String JDT_IDE_PE_CLASS = "org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeBuildProcessingEnvImpl"; private static final String JDT_BATCH_PE_CLASS = "org.eclipse.jdt.internal.compiler.apt.dispatch.BatchProcessingEnvImpl"; private static final String COMPILER_NAME_ECLIPSE_JDT = "Eclipse JDT"; private static final String MAP_STRUCT_VERSION = initMapStructVersion(); private final String runtimeVersion; private final String runtimeVendor; private final String compiler; private final boolean eclipseJDT; private final boolean javac; DefaultVersionInformation(String runtimeVersion, String runtimeVendor, String compiler) { this.runtimeVersion = runtimeVersion; this.runtimeVendor = runtimeVendor; this.compiler = compiler; this.eclipseJDT = compiler.startsWith( COMPILER_NAME_ECLIPSE_JDT ); this.javac = compiler.startsWith( COMPILER_NAME_JAVAC ); } @Override public String getRuntimeVersion() { return this.runtimeVersion; } @Override public String getRuntimeVendor() { return this.runtimeVendor; } @Override public String getMapStructVersion() { return MAP_STRUCT_VERSION; } @Override public String getCompiler() { return this.compiler; } @Override public boolean isEclipseJDTCompiler() { return eclipseJDT; } @Override public boolean isJavacCompiler() { return javac; } static DefaultVersionInformation fromProcessingEnvironment(ProcessingEnvironment processingEnv) { String runtimeVersion = System.getProperty( "java.version" ); String runtimeVendor = System.getProperty( "java.vendor" ); String compiler = getCompiler( processingEnv ); return new DefaultVersionInformation( runtimeVersion, runtimeVendor, compiler ); } private static String getCompiler(ProcessingEnvironment processingEnv) { String className = processingEnv.getClass().getName(); if ( className.equals( JAVAC_PE_CLASS ) ) { return COMPILER_NAME_JAVAC; } if ( className.equals( JDT_IDE_PE_CLASS ) ) { // the processing environment for the IDE integrated APT is in a different bundle than the APT classes return COMPILER_NAME_ECLIPSE_JDT + " (IDE) " + getLibraryName( processingEnv.getTypeUtils().getClass(), true ); } if ( className.equals( JDT_BATCH_PE_CLASS ) ) { return COMPILER_NAME_ECLIPSE_JDT + " (Batch) " + getLibraryName( processingEnv.getClass(), true ); } return processingEnv.getClass().getSimpleName() + " from " + getLibraryName( processingEnv.getClass(), false ); } private static String getLibraryName(Class<?> clazz, boolean preferVersionOnly) { String classFileName = asClassFileName( clazz.getName() ); URL resource = clazz.getClassLoader().getResource( classFileName ); Manifest manifest = openManifest( classFileName, resource ); if ( preferVersionOnly && manifest != null ) { String version = manifest.getMainAttributes().getValue( "Bundle-Version" ); if ( version != null ) { return version; } } if ( "jar".equals( resource.getProtocol() ) ) { return extractJarFileName( resource.getFile() ); } else if ( "jrt".equals( resource.getProtocol() ) ) { return extractJrtModuleName( resource ); } else if ( "bundleresource".equals( resource.getProtocol() ) && manifest != null ) { return extractBundleName( manifest ); } return resource.toExternalForm(); } private static Manifest openManifest(String classFileName, URL resource) { try { URL manifestUrl = createManifestUrl( classFileName, resource ); return new Manifest( manifestUrl.openStream() ); } catch ( IOException e ) { return null; } } private static String extractBundleName(Manifest manifest) { String version = manifest.getMainAttributes().getValue( "Bundle-Version" ); String symbolicName = manifest.getMainAttributes().getValue( "Bundle-SymbolicName" ); int semicolon = symbolicName.indexOf( ';' ); if ( semicolon > 0 ) { symbolicName = symbolicName.substring( 0, semicolon ); } return symbolicName + "_" + version; } private static String extractJrtModuleName(URL resource) { // JDK 9 style, e.g. jrt:/jdk.compiler/com/sun/tools/javac/processing/JavacProcessingEnvironment.class int moduleNameSeparator = resource.getFile().indexOf( '/', 1 ); if ( moduleNameSeparator > 1 ) { return resource.getFile().substring( 1, moduleNameSeparator ); } return resource.toExternalForm(); } private static URL createManifestUrl(String classFileName, URL resource) throws MalformedURLException { String classUrlString = resource.toExternalForm(); String baseFileUrl = classUrlString.substring( 0, classUrlString.length() - classFileName.length() ); return new URL( baseFileUrl + "META-INF/MANIFEST.MF" ); } private static String asClassFileName(String className) { return className.replace( '.', '/' ) + ".class"; } private static String extractJarFileName(String file) { int excl = file.indexOf( '!' ); if ( excl > 0 ) { String fullFileName = file.substring( 0, excl ); // it's an URL, so it's not the system path separator char: int lastPathSep = fullFileName.lastIndexOf( '/' ); if ( lastPathSep > 0 && lastPathSep < fullFileName.length() ) { return fullFileName.substring( lastPathSep + 1 ); } } return file; } private static String initMapStructVersion() { String classFileName = asClassFileName( DefaultVersionInformation.class.getName() ); URL resource = DefaultVersionInformation.class.getClassLoader().getResource( classFileName ); Manifest manifest = openManifest( classFileName, resource ); if ( null != manifest ) { String version = manifest.getMainAttributes().getValue( "Implementation-Version" ); if ( version != null ) { return version; } } return ""; } }