Package com.google.gwt.inject.rebind

Source Code of com.google.gwt.inject.rebind.GinBridgeClassLoader

/*
* Copyright 2011 Google Inc.
*
* 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 com.google.gwt.inject.rebind;

import com.google.gwt.core.ext.GeneratorContext;
import com.google.gwt.core.ext.TreeLogger;
import com.google.gwt.core.ext.TreeLogger.Type;
import com.google.gwt.dev.javac.CompilationState;
import com.google.gwt.dev.javac.CompiledClass;
import com.google.gwt.dev.javac.StandardGeneratorContext;

import java.util.Collection;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;

/**
* Gin-internal class loader that allows us to load classes generated by other generators and
* super-source.
*
* <p>In most cases, Gin needs access to the GWT version of a class (whether it is unmodified,
* created by a generator or a super-source version) so this is the default version provided by this
* class loader. The exception are JRE classes (which cannot be loaded by a custom class loader),
* classes that are also used in the Gin Generator "rebind" code (otherwise class literal
* comparisons yield strange results) and classes that are defined as super-source by Gin but must
* be their "normal" self during the generator run. These exceptions are loaded with the system
* class loader.
*
* <p>If the class is not available to GWT, we attempt to load it through the system class loader.
*
* <p>Unfortunately, GWT does not like to expose internal details like the compilation state and its
* bytes. For now, we use reflection to access this internal state but in the long term we should
* switch to other strategies such as running javac on source (which we'd need to reverse-engineer
* from parsing the GWT AST).
*/
class GinBridgeClassLoader extends ClassLoader {

  private final TreeLogger logger;
  private final GeneratorContext context;

  /**
   * Packages that should not be loaded from GWT.
   */
  private final Collection<String> exceptedPackages;

  // Lazily load class files from compilation state.
  private boolean loadedClassFiles = false;
  private Map<String, CompiledClass> classFileMap;

  GinBridgeClassLoader(GeneratorContext context, TreeLogger logger,
      Collection<String> exceptedPackages) {
    super(GinBridgeClassLoader.class.getClassLoader()); // Use own class loader.
    this.context = context;
    this.logger = logger;
    this.exceptedPackages = getExceptedPackages(exceptedPackages);
  }

  private static Collection<String> getExceptedPackages(Collection<String> superSourceExceptions) {
    Set<String> names = new LinkedHashSet<String>();
    for (String name : superSourceExceptions) {
      if (name.endsWith(".")) {
        names.add(name);
      } else {
        names.add(name + ".");
      }
    }

    // Make sure we're not loading JRE classes through a non-system class loader (which is not
    // allowed).
    names.add("java.");

    // Annotation loading will require sun.reflect APIs, even when they're referenced from
    // and present in client code.
    names.add("sun.reflect.");
    return names;
  }

  /**
   * @inheritDoc
   *
   * Gin class loading implementation, making sure that classes are loaded consistently and can be
   * GWT generated or super-source classes. See description {@link GinBridgeClassLoader above}.
   */
  @Override
  protected Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
    Class<?> clazz = findLoadedClass(name);
    if (clazz == null) {
      if (inExceptedPackage(name)) {
        clazz = super.loadClass(name, false);
      } else {
        try {
          clazz = findClass(name);         
        } catch (ClassNotFoundException e) {
          clazz = super.loadClass(name, false);
          if (!clazz.isAnnotation()) { // Annotations are always safe to load
            logger.log(Type.WARN, String.format(
                "Class %s is used in Gin, but not available in GWT client code.", name));
          }
        }
      }
    }

    if (resolve) {
      resolveClass(clazz);
    }

    return clazz;
  }

  private boolean inExceptedPackage(String name) {
    for (String pkg : exceptedPackages) {
      if (name.startsWith(pkg)) {
        return true;
      }
    }
    return false;
  }

  /**
   * Looks up classes in GWT's compilation state.
   */
  @Override
  protected Class<?> findClass(String name) throws ClassNotFoundException {
    if (!loadedClassFiles) {
      classFileMap = extractClassFileMap();
      loadedClassFiles = true;
    }

    if (classFileMap == null) {
      throw new ClassNotFoundException(name);
    }

    String internalName = name.replace('.', '/');
    CompiledClass compiledClass = classFileMap.get(internalName);
    if (compiledClass == null) {
      throw new ClassNotFoundException(name);
    }

    // Make sure the class's package is present.
    String pkg = compiledClass.getPackageName();
    if (getPackage(pkg) == null) {
      definePackage(pkg, null, null, null, null, null, null, null);
    }

    byte[] bytes = compiledClass.getBytes();
    return defineClass(name, bytes, 0, bytes.length);
  }

  /**
   * Retrieves class definitions from a {@link GeneratorContext} by downcasting.
   */
  private Map<String, CompiledClass> extractClassFileMap() {
    if (context instanceof StandardGeneratorContext) {
      StandardGeneratorContext standardContext = (StandardGeneratorContext) context;
      return standardContext.getCompilationState().getClassFileMap();
    } else {
      logger.log(TreeLogger.Type.WARN,
          String.format("Could not load generated classes from GWT context, "
              + "encountered unexpected generator type %s.", context.getClass()));
      return null;
    }
  }
}
TOP

Related Classes of com.google.gwt.inject.rebind.GinBridgeClassLoader

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.