Package com.google.gxp.compiler.servicedir

Source Code of com.google.gxp.compiler.servicedir.ScopedServiceDirectory

/*
* Copyright (C) 2008 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.gxp.compiler.servicedir;

import com.google.common.base.Function;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.google.gxp.compiler.alerts.AlertSink;
import com.google.gxp.compiler.base.Callable;
import com.google.gxp.compiler.base.ClassImport;
import com.google.gxp.compiler.base.DefaultingImportVisitor;
import com.google.gxp.compiler.base.Implementable;
import com.google.gxp.compiler.base.Import;
import com.google.gxp.compiler.base.InstanceCallable;
import com.google.gxp.compiler.base.PackageImport;
import com.google.gxp.compiler.base.TemplateName;

import java.util.*;

/**
* A {@code ServiceDirectory} that looks up resources with a specific "scope".
* This scope includes a current package, and a set of class and/or package
* imports. A {@code ScopedServiceDirectory} acts as a decorator for another
* {@code ServiceDirectory}, intercepting requests for unqualified resources
* and qualifying them with respect to its scope. Requests for fully qualified
* resources are passed through to the underlying {@code ServiceDirectory}
* without modification.
*
* <p>When attempting to resolve an unqualified reference the rules are as
* close to the rules of the Java proramming language as possible:
* <ol>
* <li>class imports have highest precedence. Class imports are not allowed to
* be ambiguous.
* <li>the current package has the next highest precedence.
* <li>package imports have the lowest precedence. Ambigous references via
* package imports are ignored. That is, if something cannot be found via a
* class import or in the current package, but can be found via two or more
* distinct package imports, then it is considered to not have been found at
* all.
* </ol>
*/
public class ScopedServiceDirectory implements ServiceDirectory {
  private final String packageName;
  private final Set<String> packageImports;
  private final Map<String, TemplateName> classImports;
  private final ServiceDirectory baseServiceDirectory;
  private final Function<TemplateName, Callable> callableGetter =
      new Function<TemplateName, Callable>() {
        public Callable apply(TemplateName from) {
          return baseServiceDirectory.getCallable(from);
        }
      };
  private final Function<TemplateName, InstanceCallable> instanceCallableGetter =
      new Function<TemplateName, InstanceCallable>() {
        public InstanceCallable apply(TemplateName from) {
          return baseServiceDirectory.getInstanceCallable(from);
        }
      };
  private final Function<TemplateName, Implementable> implementableGetter =
      new Function<TemplateName, Implementable>() {
        public Implementable apply(TemplateName from) {
          return baseServiceDirectory.getImplementable(from);
        }
      };

  public ScopedServiceDirectory(AlertSink alertSink,
                                final ServiceDirectory baseServiceDirectory,
                                String packageName,
                                List<? extends Import> imports) {
    this.baseServiceDirectory = Preconditions.checkNotNull(baseServiceDirectory);
    this.packageName = Preconditions.checkNotNull(packageName);

    ImportProcessor visitor = new ImportProcessor(alertSink, packageName);
    for (Import imp : imports) {
      imp.acceptVisitor(visitor);
    }

    this.packageImports = ImmutableSet.copyOf(visitor.getPackageImports());
    this.classImports = ImmutableMap.copyOf(visitor.getClassImports());
  }

  /**
   * @return an ImportVisitor that, upon visiting an import, validates it and
   * populates either classImports or packageImports as appropriate.
   */
  private static class ImportProcessor extends DefaultingImportVisitor<Void> {
    private final AlertSink alertSink;
    private final Set<String> packageImports = Sets.newHashSet();
    private final Map<String, TemplateName> classImports = Maps.newHashMap();

    public ImportProcessor(AlertSink alertSink, String packageName) {
      this.alertSink = Preconditions.checkNotNull(alertSink);
      packageImports.add(packageName);
    }

    public Set<String> getPackageImports() {
      return packageImports;
    }

    public Map<String, TemplateName> getClassImports() {
      return classImports;
    }

    @Override
    public Void defaultVisitImport(Import imp) {
      // do nothing
      return null;
    }

    @Override
    public Void visitClassImport(ClassImport imp) {
      TemplateName className = imp.getClassName();
      String baseName = className.getBaseName();
      TemplateName redundantImport = classImports.get(baseName);
      if ((redundantImport != null) && !className.equals(redundantImport)) {
        alertSink.add(new AmbiguousImportError(imp, baseName, redundantImport, className));
      } else {
        classImports.put(baseName, className);
      }
      return null;
    }

    @Override
    public Void visitPackageImport(PackageImport imp) {
      packageImports.add(imp.getPackageName());
      return null;
    }
  }

  private <T> T baseGet(TemplateName templateName, Function<TemplateName, T> underlyingGetter) {
    if (templateName.getPackageName() == null) {
      String baseName = templateName.getBaseName();
      if (classImports.containsKey(baseName)) {
        return underlyingGetter.apply(classImports.get(baseName));
      } else {
        templateName = baseFindUniquePackageMatch(baseName, underlyingGetter);
        if (templateName == null) {
          templateName = TemplateName.create(packageName, baseName);
        }
      }
    }
    return underlyingGetter.apply(templateName);
  }

  /**
   * @return the name of the unique callable that can be found by combining the
   * specified baseName with our package imports. If there is not a unique
   * callable, returns null.
   */
  private <T> TemplateName baseFindUniquePackageMatch(String baseName,
                                                      Function<TemplateName, T> underlyingGetter) {
    TemplateName result = null;
    for (String packageImport : packageImports) {
      TemplateName fqName = new TemplateName.FullyQualified(packageImport, baseName);
      if (underlyingGetter.apply(fqName) != null) {
        if (result != null) {
          // Ambiguous match.
          return null;
        } else {
          result = fqName;
        }
      }
    }
    return result;
  }

  public Callable getCallable(TemplateName templateName) {
    return baseGet(templateName, callableGetter);
  }

  public InstanceCallable getInstanceCallable(TemplateName templateName) {
    return baseGet(templateName, instanceCallableGetter);
  }

  public Implementable getImplementable(TemplateName templateName) {
    return baseGet(templateName, implementableGetter);
  }
}
TOP

Related Classes of com.google.gxp.compiler.servicedir.ScopedServiceDirectory

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.
document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');