content: package act.util;
  
  /*-
   * #%L
   * ACT Framework
   * %%
   * Copyright (C) 2014 - 2017 ActFramework
   * %%
   * 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.
   * #L%
   */
  
  import act.app.App;
  import act.app.AppByteCodeScanner;
  import act.app.AppByteCodeScannerBase;
  import act.app.AppSourceCodeScanner;
  import org.osgl.$;
  import org.osgl.util.C;
  import org.osgl.util.E;
  
  import java.lang.annotation.Annotation;
  import java.util.Map;
  import java.util.Set;
  
  public abstract class AnnotatedTypeFinder extends AppCodeScannerPluginBase {
  
      private $.Func2<App, String, Map<Class<? extends AppByteCodeScanner>, Set<String>>> foundHandler;
      private Class<? extends Annotation> annoType;
      private boolean noAbstract;
      private boolean publicOnly;
      private App app;
      protected Set<String> foundClasses = C.newSet();
  
      protected AnnotatedTypeFinder(boolean publicOnly, boolean noAbstract, Class<? extends Annotation> annoType, $.Func2<App, String, Map<Class<? extends AppByteCodeScanner>, Set<String>>> foundHandler) {
          E.NPE(annoType);
          this.annoType = annoType;
          this.foundHandler = foundHandler;
          this.noAbstract = noAbstract;
          this.publicOnly = publicOnly;
      }
  
      protected AnnotatedTypeFinder(Class<? extends Annotation> annoType, $.Func2<App, String, Map<Class<? extends AppByteCodeScanner>, Set<String>>> foundHandler) {
          this(true, true, annoType, foundHandler);
      }
  
      protected AnnotatedTypeFinder(Class<? extends Annotation> annoType) {
          this(true, true, annoType, null);
      }
  
      protected App app() {
          return app;
      }
  
      @Override
      public AppSourceCodeScanner createAppSourceCodeScanner(App app) {
          return null;
      }
  
      @Override
      public AppByteCodeScanner createAppByteCodeScanner(App app) {
          this.app = app;
          return new ByteCodeSensor();
      }
  
      @Override
      public boolean load() {
          return true;
      }
  
      private class ByteCodeSensor extends AppByteCodeScannerBase {
          private ClassDetector detector;
          private $.Func2<App, String, Map<Class<? extends AppByteCodeScanner>, Set<String>>> foundHandler = AnnotatedTypeFinder.this.foundHandler;
  
          @Override
          protected void reset(String className) {
              super.reset(className);
              detector = ClassDetector.of(new AnnotatedClassFilter(publicOnly, noAbstract, annoType) {
                  @Override
                  public void found(Class clazz) {
                  }
              });
          }
  
          @Override
          public ByteCodeVisitor byteCodeVisitor() {
              return detector;
          }
  
          @Override
          public void scanFinished(String className) {
              if (detector.found()) {
                  foundClasses.add(className);
                  Map<Class<? extends AppByteCodeScanner>, Set<String>> dependencies =
                          null == foundHandler ? null : foundHandler.apply(app(), className);
                  if (null != dependencies && !dependencies.isEmpty()) {
                      for (Class<? extends AppByteCodeScanner> c : dependencies.keySet()) {
                          addDependencyClassToScanner(c, dependencies.get(c));
                      }
                  }
              }
          }
  
          @Override
          protected boolean shouldScan(String className) {
              return true;
          }
  
          @Override
          public int hashCode() {
              return $.hc(detector, ByteCodeSensor.class);
          }
  
          @Override
          public boolean equals(Object obj) {
              if (obj == this) {
                  return true;
              }
              if (obj instanceof ByteCodeSensor) {
                  ByteCodeSensor that = (ByteCodeSensor)obj;
                  return $.eq(that.detector, this.detector) && $.eq(that.foundHandler, this.foundHandler);
              }
              return false;
          }
      }
  
  }
  