All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.github.lab515.utils.ClassMatcher Maven / Gradle / Ivy

There is a newer version: 1.0.12
Show newest version
package io.github.lab515.utils;

import java.util.ArrayList;
import java.util.List;

/**
 * Class Matcher replace regex to match class
 * **.Aclass*.**
 */
public class ClassMatcher {
  private String[][] segsArray   = null;  // array of segment array, each segment array matches one expression
  private int[][]    segFlagsArray = null;
  private Integer[]  posNegArray = null;  // indicate the pos | seg of every expression
  private char       sp          = '.';
  private int        max         = 0;
  private boolean    negAll      = true;
  private boolean    posAll      = true;
  private static final int T_ALL = 1, T_PART = 2, T_HEADING = 4, T_CONST = 0,T_TAILING = 8, T_TH = 12; // easier implementation
  public ClassMatcher(String[] strings) {
    this(strings, '.');
  }

  /**
   * Constructor
   *
   * @param exps     expression array. Rule is same with Ant. Starts with "-" for un-match and "+" for match.
   * @param splitter package splitter
   */
  public ClassMatcher(String[] exps, char splitter) {
    sp = splitter;
    if (exps != null && exps.length > 0) {
      List segsList = new ArrayList();     // list of segments, each of which are for each expression
      List segsFlags = new ArrayList();
      List posNegList = new ArrayList();    // indicate the pos | seg of every expression
      String[] segArray = null;
      int[] flags = null;
      char c = ' ';
      for (String exp : exps) {
        if (exp == null){
          exp = "";
        }
        else{
          exp = exp.trim();
        }
        if (exp.startsWith("-")) {
          posNegList.add(0);
          exp = exp.substring(1);
          posAll = false;
        } else {
          if (exp.startsWith("+")) {
            exp = exp.substring(1);
          }
          posNegList.add(1);
          negAll = false;
        }
        segsList.add(segArray = parseExp(exp,splitter));
        flags = new int[segArray.length];
        segsFlags.add(flags);
        int cnt = 0;
        for(int i = 0; i < flags.length;i++){
          exp = segArray[i];
          if(exp == null){
            segArray[cnt++] = null; // set it
            break; // reach the end;
          }
          else{
            c = exp.charAt(0);
            if(c == '*'){
              flags[cnt] = T_ALL;
              segArray[cnt++] = exp;
            }
            else if(c == '+'){
              flags[cnt] = T_PART;
              segArray[cnt++] = exp;
            } else{
              if(c == sp && cnt> 0 && (flags[cnt-1] & T_ALL) != 0) {
                flags[cnt - 1] |= T_HEADING;
                exp = exp.substring(1);
              }
              if(exp.length() > 0 && exp.charAt(exp.length()-1)  == sp && i< flags.length-1 && segArray[i+1] != null && segArray[i+1].charAt(0) == '*'){
                exp = exp.substring(0,exp.length()-1);
                if(exp.length() > 0){
                  segArray[cnt] = exp;
                  flags[cnt++] = T_CONST;
                }
                segArray[cnt] = "*";
                flags[cnt++] = T_ALL | T_TAILING;
                i++;
              }else if(exp.length() > 0){
                segArray[cnt] = exp;
                flags[cnt++] = T_CONST;
              }
            }
          }
        }
        if (cnt > max) {
          max = cnt;
        }
      }
      if (segsList.size() > 0) {
        segsArray = new String[segsList.size()][];
        posNegArray = new Integer[segsList.size()];
        segFlagsArray = new int[segsList.size()][];
        segsList.toArray(segsArray);
        posNegList.toArray(posNegArray);
        segsFlags.toArray(segFlagsArray);
        segsList.clear();
        posNegList.clear();
      }
    }
  }

  private static String[] parseExp(String exp, char sp) {
    if (exp == null || (exp = exp.trim()).length() < 1) {
      return new String[]{null};
    }
    if (exp.endsWith("*")) {
      exp += " ";     // to process exp == '*'
    }
    String[] segs = exp.split("\\*");
    if (segs.length < 2) {  // no '*' in the exp or only *
      segs[0] = segs[0].trim();
      return segs;
    }

    List segList = new ArrayList();
    String last = null;
    String seg = segs[0].trim();
    if (seg.length() > 0) { // segs[0].length == 0 means start with '*', then last == null
      last = seg;
      segList.add(last);
    }
    for (int i = 1; i < segs.length; i++) {
      seg = segs[i];
      // log.info "seg " + i + ":" + seg + ", last:" + last
      if (last == null || (last.charAt(0) != '*' && last.charAt(0) != '+')) {
        // optimize: **.** equals **
        if (!(seg.length() == 0 && last != null && last.length() == 1 && last.charAt(0) == sp
                && segList.size() > 1 && segList.get(segList.size() - 2).charAt(0) == '*')) {
          last = seg.length() == 0 ? "*" : "+";
          segList.add(last);
        } else {
          segList.remove(segList.size() - 1);
          last = "*";
        }
      }
      seg = seg.trim();
      if (seg.length() > 0) { // ignore space between '*', like "* *"
        last = seg;
        segList.add(last);
      }
    }
    segs = new String[segList.size() + 1];
    segList.toArray(segs);
    segList.clear();
    return segs;
  }

  public boolean matchSingle(String cls) {
    if (segsArray == null || cls == null) {
      return false;
    }
    int[] idx = new int[max];
    boolean matched = false;
    for (int i = 0; i < segsArray.length; i++) {
      if (matchClassInternal(segsArray[i], idx, segFlagsArray[i], cls)) {
        if (posNegArray[i] > 0) {
          if (posAll) {
            return true;
          }
          matched = true;
        } else {
          return false;
        }
      }
    }
    return matched || negAll;
  }

  public String[] match(String[] clses) {
    if (segsArray == null || clses == null) {
      return null;
    }
    int[] idx = new int[max];
    ArrayList ret = new ArrayList();
    int i = 0;
    int l = segsArray.length;
    boolean matched = false;
    for (String cls : clses) {
      if (cls == null) {
        continue;
      }
      matched = false;
      for (i = 0; i < l; i++) {
        if (matchClassInternal(segsArray[i], idx,segFlagsArray[i], cls)) {
          if (posNegArray[i] < 1 || posAll) {
            if (posAll) {
              ret.add(cls);
            }
            break;
          }
          matched = true;
        }
      }
      if (i >= l && (matched || negAll)) {
        ret.add(cls);
      }
    }
    String[] r = null;
    if (ret.size() > 0) {
      r = new String[ret.size()];
      ret.toArray(r);
      ret.clear();
    }
    return r;
  }

  public String[] match(List clses) {
    if (segsArray == null || clses == null) {
      return null;
    }
    int[] idx = new int[max];
    ArrayList ret = new ArrayList();
    int i = 0;
    int l = segsArray.length;
    boolean matched = false;
    for (String cls : clses) {
      if (cls == null) {
        continue;
      }
      matched = false;
      for (i = 0; i < l; i++) {
        if (matchClassInternal(segsArray[i], idx,segFlagsArray[i], cls)) {
          if (posNegArray[i] < 1 || posAll) {
            if (posAll) {
              ret.add(cls);
            }
            break;
          }
          matched = true;
        }
      }
      if (i >= l && (matched || negAll)) {
        ret.add(cls);
      }
    }
    String[] r = null;
    if (ret.size() > 0) {
      r = new String[ret.size()];
      ret.toArray(r);
      ret.clear();
    }
    return r;
  }

  private boolean matchClassInternal(String[] segs, int[] segIdx, int[] segFlags, String cls) {
    int stac = 0;
    int pos = 0;
    String seg = null;
    int lastPos = 0;
    int clen = cls.length();
    int flag = 0;
    int prePos = 0;
    int slen = 0;
    for(int i=0; i < segIdx.length;i++){segIdx[i] = -1;}
    while (stac >= 0 && stac < segs.length) {
      if(lastPos < pos) {// check if we need to fix the gap
        if(stac < 1){
          return false;
        }
        stac--;
        lastPos = segIdx[stac]; // prePos <= lastPos < pos
        flag = segFlags[stac];
        seg = segs[stac];
        if(flag == T_CONST){ // apparently not a match, and the cosnt must be after this at least
          pos = cls.indexOf(seg, pos - seg.length()); // may need to find a way to optimize the search
          segIdx[stac] = pos;
          if(pos < 0){
            return false;
          }
        }else if(flag == T_PART){
          prePos = pos;
          pos = cls.lastIndexOf(sp,pos-1);
          if(pos < 0){
            pos = 0;
          }else {
            pos++;
          }
          if(pos < lastPos){
            pos = lastPos;
          }else{
            segIdx[stac] = pos;
          }
          if(lastPos == pos){
            lastPos = pos = prePos;
            stac++;
          }
        }else {
          if((flag & T_HEADING) != 0){
            pos = cls.indexOf(sp,pos-1);
            if(pos < 0){
              pos = clen;
            }
            if(pos < clen){
              pos++;
            }
          }
          lastPos = pos; // direct match
          stac++;
        }
      }else{
        seg = segs[stac];
        if(seg == null){
          pos = clen;
          if(lastPos == pos){
            break;
          }
          continue;
        }
        flag = segFlags[stac];
        slen = 0;
        if (flag == T_CONST) {
          if(pos >= clen){
            return false;
          }
          prePos = segIdx[stac];
          if(prePos >= pos) {
            pos = prePos;
          }else {
            pos = cls.indexOf(seg, pos);
            if (pos < 0){
              return false;
            }
          }
          slen = seg.length();
        } else if(flag != T_PART && flag != T_ALL) {
          // .**/**./.**. three conditions
          flag &= T_TH;
          prePos = pos;
          if(!((prePos == 0 && flag == T_HEADING) || (prePos == clen && flag == T_TAILING))){
            prePos = prePos < clen ? cls.indexOf(sp,prePos) : -1;
            if(prePos < 0){
              if((flag & T_HEADING) != 0){
                return false;
              }
              prePos = clen;
            }
          }
          if(flag == T_HEADING) {
            segIdx[stac] = lastPos;
            slen = prePos - pos;
            if(pos < clen && (pos > 0 || cls.charAt(pos) == sp)){
              slen++;
            }
          }else {
            pos = prePos;
            if(pos < clen){
              slen = 1; // must be
            }
          }
        }
        if(lastPos == pos){
          segIdx[stac++] = pos;
          pos += slen;
          lastPos = pos;
        }
      }
    }
    return lastPos == pos && pos == clen;
  }

  public static String[] matchClasses(String[] exprs, String[] clses) {
    return matchClasses(exprs, clses, '.');
  }

  public static String[] matchClasses(String exprs, String[] clses) {
    return matchClasses(exprs, clses, '.');
  }

  public static String[] matchClasses(String exprs, String[] clses, char clsSplitter) {
    if (exprs == null || clses == null) {
      return null;
    }
    return matchClasses(exprs.split("\n"), clses, clsSplitter);
  }

  public static String[] matchClasses(String[] exprs, String[] clses, char clsSplitter) {
    if (exprs == null || clses == null) {
      return null;
    }
    ClassMatcher m = new ClassMatcher(exprs, clsSplitter);
    return m.match(clses);
  }
}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy