content: package com.alibaba.smart.framework.engine.extension.scanner;
  
  import java.io.File;
  import java.io.FileFilter;
  import java.io.IOException;
  import java.lang.annotation.Annotation;
  import java.net.JarURLConnection;
  import java.net.URL;
  import java.net.URLDecoder;
  import java.util.Enumeration;
  import java.util.HashMap;
  import java.util.LinkedHashSet;
  import java.util.Map;
  import java.util.Set;
  import java.util.jar.JarEntry;
  import java.util.jar.JarFile;
  
  import com.alibaba.smart.framework.engine.configuration.ProcessEngineConfiguration;
  import com.alibaba.smart.framework.engine.configuration.aware.ProcessEngineConfigurationAware;
  import com.alibaba.smart.framework.engine.configuration.scanner.AnnotationScanner;
  import com.alibaba.smart.framework.engine.configuration.scanner.ExtensionBindingResult;
  import com.alibaba.smart.framework.engine.exception.EngineException;
  import com.alibaba.smart.framework.engine.extension.annotation.ExtensionBinding;
  import com.alibaba.smart.framework.engine.util.ClassUtil;
  
  import lombok.Getter;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  public class SimpleAnnotationScanner implements AnnotationScanner {
  
      public static final String JAR = "jar";
      public static final String FILE = "file";
      public static final String UTF_8 = "UTF-8";
  
      private static final Logger LOGGER = LoggerFactory.getLogger(SimpleAnnotationScanner.class);
  
  
      @Getter
      private Map<String, ExtensionBindingResult> scanResult = new HashMap<String, ExtensionBindingResult>();
  
      private String[] packageNameList;
  
      public SimpleAnnotationScanner(String... packageNameList) {this.packageNameList = packageNameList;}
  
      protected static Set<Class<?>> scan(String... packageNames) throws IOException {
  
          Set<Class<?>> clazzSet = new LinkedHashSet();
  
          for (String packageName : packageNames) {
  
              String formattedPackageDirName = packageName.replace('.', '/');
  
              Enumeration<URL> allMatchedURLs = ClassUtil.getContextClassLoader().getResources(formattedPackageDirName);
  
              while (allMatchedURLs.hasMoreElements()) {
  
                  URL url = allMatchedURLs.nextElement();
                  String protocol = url.getProtocol();
  
                  if (FILE.equals(protocol)) {
                      String filePath = URLDecoder.decode(url.getFile(), UTF_8);
  
                      scanFiles(packageName, filePath, true, clazzSet);
  
                  } else if (JAR.equals(protocol)) {
  
                      JarFile jar = ((JarURLConnection)url.openConnection()).getJarFile();
  
                      Enumeration<JarEntry> entries = jar.entries();
  
                      while (entries.hasMoreElements()) {
  
                          JarEntry entry = entries.nextElement();
                          String entryName = entry.getName();
  
                          if (entryName.startsWith(formattedPackageDirName)) {
                              int idx = entryName.lastIndexOf('/');
  
                              if (idx != -1) {
  
                                  String    finalPackageName = entryName.substring(0, idx).replace('/', '.');
  
                                  if (entryName.endsWith(".class") && !entry.isDirectory()) {
  
                                      String className = entryName.substring(finalPackageName.length() + 1,
                                          entryName.length() - 6);
  
                                      clazzSet.add(ClassUtil.loadClass(finalPackageName + '.' + className));
  
                                  }
                              }
  
                          }
                      }
  
                  } else {
                      throw new EngineException("Not supported protocol: " + protocol);
                  }
              }
          }
  
  
  
          return clazzSet;
      }
  
      private static void scanFiles(String packageName, String packagePath,
                                    final boolean recursive,
                                    Set<Class<?>> classes) {
          File dir = new File(packagePath);
  
          if (!dir.exists() || !dir.isDirectory()) {
              return;
          }
  
          File[] filteredFiles = dir.listFiles(new FileFilter() {
              @Override
              public boolean accept(File file) {
                  return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
              }
          });
  
          for (File file : filteredFiles) {
  
              if (file.isDirectory()) {
                  scanFiles(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
                      classes);
              } else {
                  String className = file.getName().substring(0, file.getName().length() - 6);
  
                  classes.add(
                      ClassUtil.loadClass(packageName + '.' + className));
  
              }
          }
      }
  
      @Override
      public void clear() {
          scanResult.clear();
      }
  
      @Override
      public void scan(
          ProcessEngineConfiguration processEngineConfiguration, Class<? extends Annotation> targetAnnotationType) {
  
          Set<Class<?>> classSet;
          try {
              classSet = scan(this.packageNameList);
          } catch (IOException e) {
              throw new EngineException(e.getMessage(), e);
          }
  
          for (Class<?> currentClazz : classSet) {
  
              boolean present = currentClazz.isAnnotationPresent(targetAnnotationType);
  
              if (present) {
  
                  ExtensionBinding currentBindingAnnotation = (ExtensionBinding)currentClazz.getAnnotation(targetAnnotationType);
                  String group = currentBindingAnnotation.group();
  
                  ExtensionBindingResult extensionBindingResult = scanResult.get(group);
                  if (null == extensionBindingResult) {
  
                      extensionBindingResult = new ExtensionBindingResult();
  
                      Map<Class, Object> bindingMap = new HashMap<Class, Object>();
                      extensionBindingResult.setBindingMap(bindingMap);
  
                      scanResult.put(group, extensionBindingResult);
                  }
  
                  Map<Class, Object> bindingMap = extensionBindingResult.getBindingMap();
  
                  Class bindKeyClass = currentBindingAnnotation.bindKey();
  
                  Object objectInBindingMap = bindingMap.get(bindKeyClass);
                  if (objectInBindingMap == null) {
  
                      put(processEngineConfiguration, currentClazz, bindingMap, bindKeyClass);
  
                  } else {
                      handleDuplicatedKey( processEngineConfiguration,bindingMap,  currentBindingAnnotation, currentClazz,objectInBindingMap,targetAnnotationType);
                  }
  
              }
  
          }
  
      }
  
      private void put(ProcessEngineConfiguration processEngineConfiguration, Class<?> currentClazz,
                       Map<Class, Object> bindingMap, Class bindKeyClass) {
          boolean pecFound = false;
  
          Class tempClazz = currentClazz;
  
          while (tempClazz != null && !tempClazz.equals(Object.class)){
  
              Class<?>[] interfaces = tempClazz.getInterfaces();
              for (Class<?> anInterface : interfaces) {
                  if (anInterface.equals(ProcessEngineConfigurationAware.class)) {
                      pecFound = true;
                      break;
                  }
              }
              tempClazz = tempClazz.getSuperclass();
          }
  
          Object newInstance  = ClassUtil.createNewInstance(currentClazz);
          if (pecFound) {
              ((ProcessEngineConfigurationAware)newInstance).setProcessEngineConfiguration(
                  processEngineConfiguration);
          }
  
          bindingMap.put(bindKeyClass, newInstance);
      }
  
      protected void handleDuplicatedKey(ProcessEngineConfiguration processEngineConfiguration,Map<Class, Object> bindingMap, ExtensionBinding currentBindingAnnotation, Class targetClass,Object objectInBindingMap,Class<? extends Annotation> targetAnnotationType) {
  
          String group = currentBindingAnnotation.group();
          Class bindKeyClass = currentBindingAnnotation.bindKey();
  
          ExtensionBinding exBindingAnnotation = (ExtensionBinding)objectInBindingMap.getClass().getAnnotation(targetAnnotationType);
  
          if(currentBindingAnnotation.priority() < exBindingAnnotation.priority()){
              LOGGER.warn("Because of lower priority, current extension  {} is ignored, now using the new extension {} " ,targetClass,objectInBindingMap.getClass());
          }
  
         else if(currentBindingAnnotation.priority() == exBindingAnnotation.priority()){
              throw new EngineException(
  
                  "Duplicated bindKeyClass  found " + bindKeyClass + " for group " + group
                      + ", because of duplicated annotation or init twice.");
          }else {
  
              put(processEngineConfiguration, targetClass, bindingMap, bindKeyClass);
  
  
              LOGGER.warn("Because of higher priority, current extension  {} is replaced, now using the new extension {} " ,objectInBindingMap.getClass(),targetClass);
  
  
          }
  
  
      }
  
      @Override
      public <T> T getExtensionPoint(String group, Class<T> clazz) {
          ExtensionBindingResult extensionBindingResult = this.scanResult.get(group);
          Map<Class, Object> bindingMap = extensionBindingResult.getBindingMap();
          return (T)bindingMap.get(clazz);
      }
  
      @Override
      public Object getObject(String group, Class clazz) {
          return this.scanResult.get(group).getBindingMap().get(clazz);
  
      }
  
  }