Package at.bestsolution.efxclipse.tooling.css.cssext.parser

Source Code of at.bestsolution.efxclipse.tooling.css.cssext.parser.CssExtParser

package at.bestsolution.efxclipse.tooling.css.cssext.parser;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Set;

import org.eclipse.core.runtime.Assert;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.naming.IQualifiedNameProvider;
import org.eclipse.xtext.naming.QualifiedName;

import at.bestsolution.efxclipse.runtime.core.log.Log;
import at.bestsolution.efxclipse.runtime.core.log.Logger;
import at.bestsolution.efxclipse.tooling.css.cssDsl.ColorTok;
import at.bestsolution.efxclipse.tooling.css.cssDsl.CssTok;
import at.bestsolution.efxclipse.tooling.css.cssDsl.FuncTok;
import at.bestsolution.efxclipse.tooling.css.cssDsl.IdentifierTok;
import at.bestsolution.efxclipse.tooling.css.cssDsl.NumberTok;
import at.bestsolution.efxclipse.tooling.css.cssDsl.SymbolTok;
import at.bestsolution.efxclipse.tooling.css.cssDsl.UrlTok;
import at.bestsolution.efxclipse.tooling.css.cssDsl.WSTok;
import at.bestsolution.efxclipse.tooling.css.cssext.ICssExtManager;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRangedDoubleType;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRangedIntType;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRule;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleBracket;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleConcat;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleConcatWithoutSpace;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleFunc;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleLiteral;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleOr;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRulePostfix;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleRef;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleRegex;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleSymbol;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSRuleXor;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CSSType;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.CssExtDslPackage;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.Definition;
import at.bestsolution.efxclipse.tooling.css.cssext.cssExtDsl.PropertyDefinition;
import at.bestsolution.efxclipse.tooling.css.cssext.parser.result.NodeType;
import at.bestsolution.efxclipse.tooling.css.cssext.parser.result.ResultNode;
import at.bestsolution.efxclipse.tooling.css.cssext.parser.result.State;
import at.bestsolution.efxclipse.tooling.css.extapi.MultiProposal;
import at.bestsolution.efxclipse.tooling.css.extapi.Proposal;
import at.bestsolution.efxclipse.tooling.css.extapi.SimpleProposal;
import at.bestsolution.efxclipse.tooling.css.extapi.ValidationResult;
import at.bestsolution.efxclipse.tooling.css.util.TokUtil;

import com.google.inject.Inject;

public class CssExtParser {

  private @Inject ICssExtManager manager;
  private @Inject IQualifiedNameProvider nameProvider;
 
  private @Log("cssext.parser") Logger logger;
 
  private static enum ConsumeWS {
    MAY_CONSUME,
    MUST_CONSUME,
    NO_CONSUME;
  }
 
  private ResultNode parse(ParserInputCursor input, CSSRule rule, ConsumeWS consumeWS) {
    Assert.isNotNull(rule, "rule must not be null");
    Assert.isNotNull(input, "input must not be null");
    ResultNode result = null;
   
    switch(rule.eClass().getClassifierID()) {
    case CssExtDslPackage.CSS_RULE_BRACKET: result = parseBracket(input, (CSSRuleBracket)rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_REF: result = parseRef(input, (CSSRuleRef)rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_OR: result = parseOr(input, (CSSRuleOr) rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_XOR: result = parseConcatOr(input, (CSSRuleXor) rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_CONCAT: result = parseConcat(input, (CSSRuleConcat) rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_CONCAT_WITHOUT_SPACE: result = parseConcatWithoutSpace(input, (CSSRuleConcatWithoutSpace) rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_LITERAL: result = parseLiteral(input, (CSSRuleLiteral) rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_SYMBOL: result = parseSymbol(input, (CSSRuleSymbol) rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_POSTFIX: result = parsePostfix(input, (CSSRulePostfix) rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_REGEX: result = parseRegex(input, (CSSRuleRegex)rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_TYPE:
    case CssExtDslPackage.CSS_RANGED_DOUBLE_TYPE:
    case CssExtDslPackage.CSS_RANGED_INT_TYPE:
      result = parseType(input, (CSSType)rule, consumeWS);
      break;
    case CssExtDslPackage.CSS_RULE_FUNC: result = parseFunction(input, (CSSRuleFunc)rule, consumeWS); break;
    default:
      logger.warning(rule + " not implemented! (classifierID="+rule.eClass().getClassifierID()+")");
    }
//    if (relevant(rule)) {
//      print(g.depth, g.node, "<- " + result);
//      g.depth--;
//    }
    return result;
  }
 
  private ResultNode parseOr(ParserInputCursor input, CSSRuleOr or, ConsumeWS consumeWS) {
    ResultNode orResult = new ResultNode(NodeType.OR);
    orResult.remainingInput = input.copy();
    orResult.status = State.FORWARD;
   
    for (CSSRule rule : or.getOrs()) {
      ResultNode result = parse(input.copy(), rule, consumeWS);
      orResult.next.add(result);
    }
   
    return orResult;
  }
 
  // Concat
  private ResultNode parseConcat(ParserInputCursor l, CSSRuleConcat r, ConsumeWS consumeWS) {
    ResultNode concatResult = new ResultNode(NodeType.CONCAT);
    concatResult.remainingInput = l.copy();
    concatResult.status = State.FORWARD;
   
    Iterator<CSSRule> ruleIt = r.getConc().iterator();
    while (ruleIt.hasNext()) {
      final CSSRule rule = ruleIt.next();
      final boolean isFirstRule = r.getConc().indexOf(rule) == 0;
      for (ResultNode last : concatResult.findLast()) {
        if (last.isValid()) {
          last.next.add(parse(last.remainingInput, rule, isFirstRule?consumeWS:ConsumeWS.MUST_CONSUME));
         
        }
      }

     
    }
   
    return concatResult;
  }
 
  // Concat without space
  private ResultNode parseConcatWithoutSpace(ParserInputCursor l, CSSRuleConcatWithoutSpace r, ConsumeWS consumeWS) {
    ResultNode concatResult = new ResultNode(NodeType.CONCAT_WITHOUT_SPACE);
    concatResult.remainingInput = l.copy();
    concatResult.status = State.FORWARD;
   
    Iterator<CSSRule> ruleIt = r.getConc().iterator();
    while (ruleIt.hasNext()) {
      final CSSRule rule = ruleIt.next();
      final boolean isFirstRule = r.getConc().indexOf(rule) == 0;
      for (ResultNode last : concatResult.findLast()) {
        if (last.isValid()) {
          last.next.add(parse(last.remainingInput, rule, isFirstRule?consumeWS:ConsumeWS.NO_CONSUME));
        }
        else if (!isFirstRule && last.status == State.PROPOSE) {
//          // if the last was a proposal we go on with empty input to get multiple proposals in sequence
          ResultNode node = parse(ParserInputCursor.emptyParserInputCursor(), rule, ConsumeWS.NO_CONSUME);
          node.findByState(State.PROPOSE);
          for (ResultNode n : node.findLast()) {
            n.proposal = wrapMultiProposal(last.proposal, n.proposal);
          }
          last.next.add(node);
        }
       
      }
    }
   
    return concatResult;
  }
 
  private ResultNode parseBracket(ParserInputCursor in, CSSRuleBracket rule, ConsumeWS consumeWS) {
    return parse(in, rule.getInner(), consumeWS);
   
//    ResultNode result = new ResultNode();
//    result.nodeSymbol = "[]";
//    result.nodeName = "Bracket";
//    result.remainingInput = in.copy();
//    result.status = State.FORWARD;
//   
//    result.next.add(parse(in.copy(), rule.getInner(), consumeWS));
//    return result;
  }
 
  private ResultNode parsePostfix(ParserInputCursor in, CSSRulePostfix r, ConsumeWS consumeWS) {
    ResultNode result = null;
    if( r.getCardinality() != null && ! r.getCardinality().isEmpty() ) {
      switch (r.getCardinality().charAt(0)) {
      case '?':
        result = parseOptional(in, r.getRule(), consumeWS);
        break;
      case '*':
        result = parseStar(in, r.getRule(), consumeWS);
        break;
      case '+':
        result = parsePlus(in, r.getRule(), consumeWS);
        break;
     
    }
    return result;
  }
 
  private ResultNode parseOptional(ParserInputCursor in, CSSRule rule, ConsumeWS consumeWS) {
    ResultNode result = new ResultNode(NodeType.OPTIONAL);
    result.remainingInput = in.copy();
    result.status = State.FORWARD;
   
    result.next.add(ResultNode.createSkipNode(result));
    result.next.add(parse(in.copy(), rule, consumeWS));
   
    return result;
  }
 
  private ResultNode parseStar(ParserInputCursor in, CSSRule rule, ConsumeWS consumeWS) {
    ResultNode result = new ResultNode(NodeType.STAR);
    result.status = State.FORWARD;
    result.remainingInput = in.copy();
   
    Queue<ResultNode> last = new LinkedList<>();
    last.addAll(result.findLast());
   
    boolean first = true;
   
    while (!last.isEmpty()) {
      ResultNode cur = last.poll();
      if (cur.isValid()) {
        ResultNode n = parse(cur.remainingInput.copy(), rule, consumeWS);
       
        last.addAll(n.findLast());
       
        if (first) {
          cur.next.add(n);
         
          ResultNode skipStarNode = new ResultNode(NodeType.STAR);
          skipStarNode.status = State.SKIP;
          skipStarNode.remainingInput = cur.remainingInput.copy();
         
          cur.next.add(skipStarNode);
         
          first = false;
        }
        else {
          ResultNode starNode = new ResultNode(NodeType.STAR);
          starNode.status = State.FORWARD;
          starNode.remainingInput = cur.remainingInput.copy();
          starNode.next.add(n);
          cur.next.add(starNode);
         
          ResultNode skipStarNode = new ResultNode(NodeType.STAR);
          skipStarNode.status = State.SKIP;
          skipStarNode.remainingInput = cur.remainingInput.copy();
         
          starNode.next.add(skipStarNode);
        }
       
       
        //cur.next.add(ResultNode.createSkipNode(cur));
      }
     
    }
 
    return result;
  }
 
 
  private ResultNode parsePlus(ParserInputCursor in, CSSRule rule, ConsumeWS consumeWS) {
    ResultNode result = new ResultNode(NodeType.PLUS);
    result.status = State.FORWARD;
    result.remainingInput = in.copy();
   
    // 1 - n
    Queue<ResultNode> last = new LinkedList<>();
    last.addAll(result.findLast());
    int iteration = 0;
    while (!last.isEmpty()) {
      ResultNode cur = last.poll();
      if (cur.isValid()) {
       
        ResultNode n = parse(cur.remainingInput.copy(), rule, consumeWS);
        cur.next.add(n);
       
        if (iteration >= 1) {
          cur.next.add(ResultNode.createSkipNode(cur));
        }
       
        iteration ++;
      }
     
    }
 
    return result;
  }
 
 
  private ResultNode parseType(ParserInputCursor l, CSSType type, ConsumeWS consumeWS) {
    ResultNode result = null;
    if ("@NUM".equals(type.getType())) {
      result = parseNUMType(l, type, consumeWS);
    }
    else if ("@INT".equals(type.getType())) {
      result = parseINTType(l, type, consumeWS);
    }
    else if ("@URL".equals(type.getType())) {
      result = parseURLType(l, type, consumeWS);
    }
    else {
      logger.error("type " + type + " not supported");
      throw new UnsupportedOperationException("type " + type + " not supported");
    }
   
    return result;
  }
 
  private ResultNode parseRegex(ParserInputCursor l, CSSRuleRegex r, ConsumeWS consumeWS) {
    ResultNode result = new ResultNode(NodeType.REGEX);
   
    String regex = r.getRegex().replaceAll("\\$", "");
   
   
    ParserInputCursor localInput = l.copy();

    CssTok tok = localInput.pollNextToken();
    int wsConsumed = 0;
    if (consumeWS == ConsumeWS.MAY_CONSUME) {
      while (tok instanceof WSTok) {
        tok = localInput.pollNextToken();
        wsConsumed++;
      }
    }
   
    if (consumeWS == ConsumeWS.MUST_CONSUME) {
      if (wsConsumed > 0) {
        // ok
      }
      else {
        // invalid
        result.status = State.INVALID;
       
        return result;
      }
     
    }
   
    logger.debug("REGEX1 -> " + regex + " / / " + tok);
   
    if (tok instanceof ColorTok) {
      String s = ((ColorTok)tok).getValue();
      logger.debug("REGEX2 -> " + regex + " / / " + s);
      if (s.matches(regex)) {
        result.status = State.MATCH;
        result.matched = tok;
        result.remainingInput = localInput;
      }
      else {
        result.status = State.INVALID;
        result.remainingInput = localInput;
      }
    }
    else {
      result.status = State.INVALID;
      result.message = "expected color token";
    }
   
    return result;
  }
 
  private ResultNode parseRef(ParserInputCursor l, CSSRuleRef r, ConsumeWS consumeWS) {
    CSSRule rule =  manager.resolveReference(r);
    if (rule == null) {
      logger.debug("resolving rule ref " + r.getRef().getName() + " returned null (maybe a function?) !!!!!");
      ResultNode inv = new ResultNode(NodeType.REF);
      inv.status = State.INVALID;
      return inv;
    }
   
    ResultNode rv = parse(l, rule, consumeWS);
   
    Definition ref = r.getRef();
    QualifiedName fqn = nameProvider.getFullyQualifiedName(ref);
   
    if (l.isConsumedOrOnlyWSLeft()) {
      List<Proposal> contributedProposalsForRule = manager.getContributedProposalsForRule(fqn.toString());
      for (Proposal c : contributedProposalsForRule) {
        ResultNode e = new ResultNode(NodeType.REF);
        e.status = State.PROPOSE;
        e.proposal = c;
        rv.next.add(e);
      }
    }
   
    return rv;
  }
 
  // Xor
  // TODO check rule
  private ResultNode parseConcatOr(ParserInputCursor l, CSSRuleXor r, ConsumeWS consumeWS) {
   
//    ResultNode result = new ResultNode(NodeType.CONCAT_OR);
//    result.status = State.INVALID;
//   
//    // TODO impl this
//    for (CSSRule rule : r.getXors()) {
//      parse(l, rule, consumeWS);
//    }
//       
//    return result;
   
    ResultNode concatOrResult = new ResultNode(NodeType.CONCAT_OR);
    concatOrResult.remainingInput = l.copy();
    concatOrResult.status = State.FORWARD;
   
    Queue<CSSRule> rulesLeft = new LinkedList<>(r.getXors());
   
    int maxTests = rulesLeft.size();
    int testNo = 1;
   
    while (!rulesLeft.isEmpty() && testNo <= maxTests) {
      // pick rule
      final CSSRule rule = rulesLeft.poll();
     
      boolean match = false;
      //try on all
      for (ResultNode last : concatOrResult.findLast()) {
        if (last.isValid()) {
          ResultNode path = parse(last.remainingInput, rule, consumeWS);
          for (ResultNode pathEnd : path.findLast()) {
            if (pathEnd.isValid()) {
              match = true;
            }
          }
          last.next.add(path);
          if (!rulesLeft.isEmpty()) {
            // special skip node
            ResultNode skippy = new ResultNode(NodeType.CONCAT_OR);
            skippy.status = State.SKIP;
            skippy.remainingInput = last.remainingInput.copy();
            last.next.add(skippy);
          }
        }
      }

      if (!match) {
        // put the rule back
        rulesLeft.offer(rule);
        testNo++;
      }
      else {
        testNo = 1;
        maxTests = rulesLeft.size();
      }
    }
   
    return concatOrResult;
  }
 

 
 
 
//  private final int n = Integer.MAX_VALUE;
 
//  private boolean recParsePostfix(ParsePath g, Set<Input> input, CSSRule rule, int min, int cur,  int max, Set<ParseResult> result) {
//    if (cur > max) return true;
//   
//    print(g.depth, "recPostfix for " + input);
//      for (Input in : input) {
//        print(g.depth, "recPostfix " + in);
//        ParseResult localResult = parse(g, in, rule);
//       
//        if (localResult.status.getCode() == IStatus.CANCEL) {
//          result.add(localResult);
//        }
//       
//        if (!in.isConsumed()) {
//          // consume ws
//          print(g.depth, "consuming ws");
//          CssTok ws = in.getNextTok();
//          if (!(ws instanceof WSTok)) {
//            localResult.status = Status.CANCEL_STATUS;
////            continue;
//          }
//        }
//       
//        boolean done = recParsePostfix(g, localResult.remainingInput, rule, min, cur + 1, max, result);
//       
//        if (done) {
//         
//        }
//    }
//     
//    return false;
//  }
 
//  private boolean checkAllInvalid(List<ParseResult> results) {
//    boolean  invalid = true;
//   
//    for (ParseResult r : results) {
//      if (r.status != Status.INVALID) {
//        invalid = false;
//        break;
//      }
//    }
//   
//    return invalid;
//  }
 
 
  private Proposal createProposal(final String proposal) {
    return new Proposal() {
     
      @Override
      public String getProposal() {
        return proposal;
      }
     
      @Override
      public int getPriority() {
        return 0;
      }
     
      @Override
      public String getLabel() {
        return proposal;
      }
     
      @Override
      public String getImageUrl() {
        return null;
      }
     
      @Override
      public Object getAdditionalInfo() {
        return null;
      }
     
      @Override
      public Type getType() {
        return Type.Value;
      }
     
      @Override
      public String toString() {
        return proposal;
      }
    };
  }
 
  private Proposal wrapMultiProposal(final Proposal previous, final Proposal proposal) {
    return new MultiProposal() {
     
      @Override
      public Type getType() {
        return proposal.getType();
      }
     
      @Override
      public String getProposal() {
        return previous.getProposal() + proposal.getProposal();
      }
     
      @Override
      public int getPriority() {
        return proposal.getPriority();
      }
     
      @Override
      public String getLabel() {
        return previous.getLabel() + proposal.getLabel();
      }
     
      @Override
      public String getImageUrl() {
        return proposal.getImageUrl();
      }
     
      @Override
      public Object getAdditionalInfo() {
        return proposal.getAdditionalInfo();
      }
     
      @Override
      public Proposal getPrevious() {
        return previous;
      }
     
      @Override
      public String toString() {
        return previous.toString() + " + " + proposal.toString();
      }
    };
  }
 
  private ResultNode parseFunction(ParserInputCursor in, CSSRuleFunc ruleFunc, ConsumeWS consumeWS) {
    ResultNode result = new ResultNode(NodeType.FUNCTION);
   
    ParserInputCursor local = in.copy();
   
    CssTok tok;
    try {
      tok = consumeWS(local, consumeWS);
     
      if (tok == null) {
        result.status = State.PROPOSE;
        result.proposal = createProposal(ruleFunc.getName() + "()");
      }
      else {
        if (tok instanceof FuncTok) {
          // ok
          FuncTok fTok = (FuncTok) tok;
          String funcName = fTok.getName().getName();
         
          if (funcName.equals(ruleFunc.getName())) {
            result.status = State.MATCH; // func name ok, still need to match params
            result.matched = tok;
           
            // TODO add parsing of function parameters
//            ParserInput funcInput = new ParserInput(fTok.getParams());
           
           
//            List<ParseResult> paramResult = parse(g, funcInput.createCursor(), ruleFunc.getParams(), true);
           
//            logger.debug("paramResult= " + paramResult);
           
//            boolean paramsOk = false;
//           
//            for (ParseResult paramR : paramResult) {
//              if (paramR.status == Status.MATCH && paramR.remainingInput.isConsumed()) {
//                paramsOk = true;
//                break;
//              }
//            }
//           
//            if (paramsOk) {
//              r.remainingInput = local;
//              r.status = Status.MATCH;
//             
//              g.newSegment(ParseStatus.MATCH, fTok);
//            }
//            else {
//              // function parameters wrong
//              r.message = "recheck function params";
//              r.status = Status.INVALID;
//             
//              g.newSegment(ParseStatus.INVALID, fTok, "wrong parameters");
//            }
          }
          else {
            // invalid (wrong func)
            result.message = "expected " + ruleFunc.getName();
            result.status = State.INVALID;
          }
         
         
         
        }
        else {
          // invalid
          result.status = State.INVALID;
          result.message = "expected function";
        }
      }
     
    } catch (Exception e) {
      result.status = State.INVALID;
      result.message = "expected WS";
    }
   
    result.remainingInput = local.copy();
    return result;
  }
 
  private ResultNode parseURLType(ParserInputCursor input, CSSType rule, ConsumeWS consumeWS) {
    final ResultNode result = new ResultNode(NodeType.TYPE_URL);
    ParserInputCursor local = input.copy();
    try {
      CssTok tok = consumeWS(local, consumeWS);
      if (tok != null) {
        if (tok instanceof UrlTok) {
          result.status = State.MATCH;
          result.matched = tok;
          result.remainingInput = local.copy();
        }
        else {
          result.status = State.INVALID;
          result.message = "expected url";
        }
      }
      else {
        result.status = State.PROPOSE;
        result.proposal = createProposal("url(http://efxclipse.org/)");
      }
    }
    catch (Exception e) {
      result.status = State.INVALID;
      result.message = "expected WS";
    }
   
    return result;
  }
 
  private ResultNode parseINTType(ParserInputCursor l, CSSType rule, ConsumeWS consumeWS) {
    final ResultNode result = new ResultNode(NodeType.TYPE_INT);

    ParserInputCursor local = l.copy();
    try {
      CssTok tok = consumeWS(local, consumeWS);
      if (tok != null) {
        if (tok instanceof NumberTok) {
         
          NumberTok numberTok = (NumberTok) tok;
          double number = numberTok.getVal();
         
          if (rule instanceof CSSRangedIntType) {
            CSSRangedIntType ranged = (CSSRangedIntType) rule;
            int min = ranged.getFrom();
            int max = ranged.getTo();
           
            if (min <= number && number <= max) {
              // ok
              result.remainingInput = local;
              result.status = State.MATCH;
              result.matched = tok;
            }
            else {
              // invalid range
              result.status = State.INVALID;
              result.message = "invalid range";
             
            }
          }
          else {
            // ok
            result.remainingInput = local;
            result.status = State.MATCH;
            result.matched = tok;
          }
        }
        else {
          // invalid
          result.status = State.INVALID;
         
        }
      }
      else {
        result.status = State.PROPOSE;
        result.proposal = createProposal("0");
      }
    }
    catch (Exception e) {
      result.status = State.INVALID;
      result.message = e.getMessage();
    }
 
    return result;
  }
 
  private ResultNode parseNUMType(ParserInputCursor l, CSSType rule, ConsumeWS consumeWS) {
    ResultNode result = new ResultNode(NodeType.TYPE_NUM);
   
    ParserInputCursor local = l.copy();
   
    try {
      CssTok tok = consumeWS(local, consumeWS);
      if (tok != null) {
        if (tok instanceof NumberTok) {
          // ok
          NumberTok numberTok = (NumberTok) tok;
          double number = numberTok.getVal();
         
          if (rule instanceof CSSRangedDoubleType) {
            CSSRangedDoubleType ranged = (CSSRangedDoubleType) rule;
            double min = ranged.getFrom();
            double max = ranged.getTo();
           
            if (min <= number && number <= max) {
              // ok
              result.remainingInput = local;
              result.status = State.MATCH;
              result.matched = tok;
            }
            else {
              // invalid range
              result.status = State.INVALID;
              result.message = "invalid range";
            }
          }
          else {
            // ok
            result.remainingInput = local;
            result.status = State.MATCH;
            result.matched = tok;
          }
        }
        else {
          // invalid
          result.status = State.INVALID;
         
        }
      }
      else {
        result.status = State.PROPOSE;
        result.proposal = createProposal("0.0");
      }
    }
    catch (Exception e) {
      result.status = State.INVALID;
      result.message = e.getMessage();
    }
   
    return result;
  }
 
  private CssTok consumeWS(ParserInputCursor input, ConsumeWS consumeWS) throws Exception {
   
    CssTok tok = input.pollNextToken();
    int wsConsumed = 0;
    if (consumeWS == ConsumeWS.MAY_CONSUME || consumeWS == ConsumeWS.MUST_CONSUME) {
      while (tok instanceof WSTok) {
        tok = input.pollNextToken();
        wsConsumed++;
      }
    }
   
    if (consumeWS == ConsumeWS.MUST_CONSUME) {
      if (wsConsumed > 0) {
        // ok
      }
      else {
        // invalid
        throw new Exception("expected whitespace");
      }
     
    }
   
    return tok;
   
  }

  // Literal
  private ResultNode parseLiteral(ParserInputCursor input, CSSRuleLiteral r, ConsumeWS consumeWS) {
    ResultNode result = new ResultNode(NodeType.LITERAL);
   
    ParserInputCursor local = input.copy();
   
    try {
      CssTok tok = consumeWS(local, consumeWS);
     
      if (tok != null) {
        String literal = r.getValue();
       
        if (matchCssTok(tok, literal)) {
          result.status = State.MATCH;
          result.matched = tok;
          result.remainingInput = local.copy();
         
        }
        else {
          result.status = State.INVALID;
          result.message = "expected " + literal;
        }
      }
      else {
        result.status = State.PROPOSE;
        result.proposal = createProposal(r.getValue());
      }
     
    }
    catch (Exception e) {
      result.status = State.INVALID;
      result.message = e.getMessage();
    }
   
   
   
   
    return result;
   
  }
 
  private ResultNode parseSymbol(ParserInputCursor l, CSSRuleSymbol r, ConsumeWS consumeWS) {
    final ResultNode result = new ResultNode(NodeType.SYMBOL);
   
    // This workaround allows symbol concatenation without whitespace
    if (consumeWS == ConsumeWS.MUST_CONSUME) {
      consumeWS = ConsumeWS.MAY_CONSUME;
    }
   
    final ParserInputCursor local = l.copy();
   
    try {
      CssTok tok = consumeWS(local, consumeWS);
      if (tok != null) {
        String symbol = r.getSymbol();
        if (matchCssTok(tok, symbol)) {
          // valid yay!
          result.status = State.MATCH;
          result.matched = tok;
          result.remainingInput = local.copy();
         
        }
        else {
          result.status = State.INVALID;
          result.message = "expected " + symbol;
        }
      }
      else {
        result.status = State.PROPOSE;
        result.proposal = createProposal(r.getSymbol());
      }
    }
    catch (Exception e) {
      result.status = State.INVALID;
      result.message = e.getMessage();
    }
   
    return result;
   
  }
 
  private boolean matchCssTok(CssTok tok, String literal) {
    if (tok instanceof IdentifierTok) {
      return literal.equals(((IdentifierTok)tok).getName());
    }
    else if (tok instanceof SymbolTok) {
      return literal.equals(((SymbolTok)tok).getSymbol());
    }
    return false;
  }
 
  public List<Proposal> findProposals(String element, String propertyName, List<CssTok> prefixToks, String prefix) {
   
    // TODO for now we skip the prefixstring queries
    //if (prefix.length() > 0) return Collections.emptyList();
   
    // debug output
    logger.debugf("findProposals( %s, %s, %s )", element, propertyName, prefix);
    logger.debugf("prefixString: '%s'", prefix);
    if (prefixToks.isEmpty()) {
      logger.debug("prefixToks: none");
    }
    else {
      logger.debugf("prefixToks:");
      Iterator<CssTok> iterator = prefixToks.iterator();
      while (iterator.hasNext()) {
        logger.debugf(" * %s", TokUtil.toString(iterator.next()));
      }
    }
   

    List<Proposal> result = new ArrayList<Proposal>();

    PropertyDefinition def = manager.findPropertyByName(propertyName);
    if (def != null) {
      final ParserInput input = new ParserInput(prefixToks);
     
      final ParserInputCursor cursor = input.createCursor();
     
      logger.debugf("starting with input: %s", cursor);
      final long parseBegin = System.nanoTime();
      final ResultNode res = parse(cursor, def.getRule(), ConsumeWS.MAY_CONSUME);
      final long parseDuration = System.nanoTime() - parseBegin;
      logger.debugf("parse needed %2.3fms returnd with %s", parseDuration*10e-6, res);
     
      for (ParseResultListener l : resultListener) {
        l.parseFinished(res);
      }
     
      //ResultNode.dbg(res);
     
     
      // add semicolon if input was successfully parsed
      // THIS IS SIMPLY WRONG!!!!
      for (ResultNode x : res.findByState(State.MATCH)) {
        if (x.remainingInput.isConsumedOrOnlyWSLeft()) {
          result.add(new SimpleProposal(";"));
          break;
        }
      }
     
      result.addAll(mapProposals(res));
     
     
//      result.addAll(findProposals(new LinkedList<CssTok>(prefixToks), prefix, def.getRule()));
    }
    else {
      result.add(createProposal("> no rule for " + propertyName + " found!"));
    }
   
    logger.debugf("-> returning %s", result);
   
    return result;
  }
 
  private List<Proposal> mapProposals(ResultNode result) {
    List<Proposal> proposals = new ArrayList<Proposal>();
   
    List<Proposal> drop = new ArrayList<>();
   
    for (ResultNode r : result.findByState(State.PROPOSE)) {
     
      if (!r.next.isEmpty()) {
        ResultNode cur = r;
        List<ResultNode> allSubPropose = cur.findByState(State.PROPOSE);
        allSubPropose.remove(cur);
        for (ResultNode n : allSubPropose) {
          if (!(n.proposal instanceof MultiProposal)) {
            drop.add(n.proposal);
          }
        }
       
      }
     
      proposals.add(r.proposal);
    }
    logger.debugf("Dropping %s", drop);
    proposals.removeAll(drop);
   
    return proposals;
  }
 
  public List<ValidationResult> validateProperty(String element, String propertyName, List<CssTok> tokens) {
   
    // debug output
    logger.debugf("validateProperty( %s, %s )", element, propertyName);
    if (tokens.isEmpty()) {
      logger.debugf("tokens: none");
    }
    else {
      logger.debug("tokens:");
      Iterator<CssTok> iterator = tokens.iterator();
      while (iterator.hasNext()) {
        logger.debugf(" * %s", TokUtil.toString(iterator.next()));
      }
    }
   
    PropertyDefinition def = manager.findPropertyByName(propertyName);
    if (def != null) {
     
//      ParserInput input = new ParserInput(tokens);
     
     
//      ResultNode res = parse(input.createCursor(), def.getRule(), ConsumeWS.MAY_CONSUME);
     
      boolean valid = false;
     
//      int lastInputIndex = -1;
      EObject lastToken = null;
//      String msg = "so ned!";
     
//      for (ParseResult p : res) {
//        if (p.status == Status.PROPOSE) continue;
//        logger.debugf(" * %s", p.toString());
//        if (p.remainingInput != null) {
//          if (p.remainingInput.isConsumed()) {
//            logger.debug("  -> der da!");
//            if (p.status == Status.MATCH || p.status == Status.SKIP) {
//              valid = true;
//              break;
//            }
//          }
//          else {
//            int newlastInputIndex = p.remainingInput.getPosition();
//            lastInputIndex = Math.max(newlastInputIndex, lastInputIndex);
//            msg = p.message;
//            lastToken = p.remainingInput.peekNextToken();
//          }
//        }
//      }
     
     
      logger.debug("valid = " + valid);
      logger.debug("lastToken = " + lastToken);
     
      List<ValidationResult> results = new ArrayList<ValidationResult>();
     
//      if (g.getOverallStatus() == ParseStatus.MATCH) {
//        return Collections.emptyList();
//      }
//      else {
//        List<ParsePathTerminal> rr = g.getMessage(g);
//        for (ParsePathTerminal s : rr) {
//          ValidationResult r = new ValidationResult(ValidationStatus.ERROR, s.message, s.inputToken, null, -1);
//          results.add(r);
//        }
//       
//      }
     
      return results;
     
//      if (valid) {
//        return Collections.emptyList();
//      }
//      else {
//        List<ValidationResult> results = new ArrayList<ValidationResult>();
//        logger.debug("returning validation result");
//        if (lastToken != null) {
//         
//          ValidationResult r = new ValidationResult(ValidationStatus.ERROR, msg, lastToken, null, 0);
//          results.add(r);
//        }
//        else {
//          ValidationResult r = new ValidationResult(ValidationStatus.ERROR, msg, null, null, lastInputIndex);
//          results.add(r);
//        }
//       
//        return results;
//      }
     
    }
   
   
    return null;
  }
 
  public static interface ParseResultListener {
    public void parseFinished(ResultNode result);
  }
 
  private Set<ParseResultListener> resultListener = new HashSet<>();
  public void addParseResultListener(ParseResultListener l) {
    resultListener.add(l);
  }
}
TOP

Related Classes of at.bestsolution.efxclipse.tooling.css.cssext.parser.CssExtParser

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