001    package com.saelist.stx;
002    
003    import com.saelist.stx.parser.*;
004    import java.util.*;
005    import org.apache.log4j.*;
006    
007    /**
008    */
009    public class Validator {
010    
011      public static Logger logger = Logger.getLogger(Validator.class);
012    
013      String textFileName;
014      public void setTextFileName(String textFileName) {
015        this.textFileName = textFileName;
016      }
017    
018      String schemaFileName;
019      public void setSchemaFileName(String schemaFileName) {
020        this.schemaFileName = schemaFileName;
021      }
022    
023      int errnum;
024    
025      /**
026        Mathces a pattern pair to a candidate pair.<p>
027    
028        Alternate ("|") matches a pair if any of its children matches it. <p>
029    
030        Another pattern matches a candidate pair if the text of the pattern
031        pair matches the text of the candidate pair and the children of the
032        pattern pair match the children of the candidate pair.<p>
033    
034        BUG: '*' and '?' alternatives do not matched empty lists correctly.
035        a[ |[ b c* ] ] does not match a[ ] correctly, though it matches a[ b ],
036        a[ c ], a[ c c ] etc.
037    
038        a[ b |[ c d* ] ] should match a[ b ] but doesn't.
039    
040    
041        @throws NullPointerException if either pair is null.
042    
043      */
044    
045      public boolean matches(Pair sn, Pair cn) {
046    
047        if(sn.getText().equals("|")) {
048          for(Iterator it = sn.getPairs(); it.hasNext(); )
049            if(matches((Pair) it.next(), cn))
050              return true;
051          return false;
052    
053        }
054    
055    
056        TestNode tn = new TestNode(sn.getText());
057        logger.debug("matches/2: schema=" + getChain(sn) + " , candidate=" + getChain(cn));
058        if( cn.getText().matches(tn.test) &&
059            matches(getChildren(sn), 0, getChildren(cn), 0)) {
060          return true;
061        }
062        return false;
063      }
064    
065      /**
066    
067      A list of patterns s is matched against a list of
068      candidates c. For each pattern, min and max specify how often
069      it must match successive elements of c.
070    
071      The match succeeds if both lists are empty.
072    
073      The match fails if the patterns are empty but candidates remain.
074    
075      The match succeeds if the first pattern matches between min and max
076      number of successibve nodes in c and the remaining patterns
077      match the remaining candidates.
078    
079      Otherwise the match fails.
080    
081      */
082    
083      public boolean matches(List s, int is, List c, int ic) {
084        logger.debug("matches/4: s=" + log(s, is) + ", c=" + log(c, ic));
085        if(is == s.size() && ic == c.size())
086          return true;
087        if(is == s.size()) {
088          return false;
089        }
090    
091        Pair sn = (Pair) s.get(is);
092        TestNode tn = new TestNode(sn.getText());
093    
094        int n = 0;
095        while(n <= tn.max && ic + n < c.size() && matches(sn, (Pair) c.get(ic + n)))
096          n++;
097    
098        while(n >= tn.min) {
099          if(matches(s, is + 1, c, ic + n))
100            return true;
101          n--;
102        }
103    
104        logger.warn("matches/4: Cannot match " + log(s, is) + " with " + log(c, ic));
105    
106        logerr(schemaFileName, s, is, errnum);
107        logerr(textFileName, c, ic, errnum++);
108    
109        return false;
110      }
111    
112    
113      // Print a standard error line that can be parsed by jedit. Example:
114      // file:C:/home/thor/projekt/stx-0.1/build-stx.xml:59: Compile failed; see the compiler error output for details.
115      void logerr(String filename, List n, int in, int errnum) {
116        if(in >= n.size())
117          return;
118        if(filename == null)
119          return;
120        if(! (n.get(in) instanceof Pair))
121          return;
122    
123        Pair pp = (Pair) n.get(in);
124        System.err.println("file:" + filename + ":" + pp.getSrcLine() + ": col " + pp.getSrcColumn() + " No match. Err " + errnum);
125        System.err.println("path: " + getChain(pp));
126      }
127    
128    
129      String log(List n, int in) {
130        if(in >= n.size())
131          return "{}";
132        Pair p = (Pair) n.get(in);
133        if(p instanceof Pair) {
134          Pair pp = (Pair) p;
135          return getChain(p) + " line: " + pp.getSrcLine() + " col: " + pp.getSrcColumn();
136        }
137        return getChain(p);
138      }
139    
140      String getChain(Pair s) {
141        if(s == null)
142          return "{}";
143        if(s.getParent() == null)
144          return s.getText();
145        return getChain(s.getParent()) + "/" + s.getText();
146      }
147    
148      private void log(String message) {
149        logger.info(message);
150      }
151    
152      public static class TestNode {
153        int min;
154        int max;
155        String test;
156        public TestNode(String schematext) {
157          int pos = schematext.lastIndexOf(" ");
158          if(pos == -1) {
159            min = 1;
160            max = 1;
161            test = schematext;
162          } else {
163            test = schematext.substring(0, pos);
164            String quantifier = schematext.substring(pos + 1);
165            if(quantifier.equals(""))
166              throw new RuntimeException("Space not allowed at the end of tests '" + schematext + "'.");
167            if(quantifier.equals("1")) {
168              min = 1;
169              max = 1;
170            } else if(quantifier.equals("?")) {
171              min = 0;
172              max = 1;
173            } else if(quantifier.equals("+")) {
174              min = 1;
175              max = Integer.MAX_VALUE;
176            } else if(quantifier.equals("*")) {
177              min = 0;
178              max = Integer.MAX_VALUE;
179            } else {
180              min = 1;
181              max = 1;
182              test = schematext;
183            }
184          }
185        }
186    
187        public String toString() {
188          return test + " " + min + "-" + max;
189        }
190      }
191    
192      List getChildren(Pair p) {
193        List list = new ArrayList(p.size());
194        for(Iterator it = p.getPairs(); it.hasNext(); )
195          list.add(it.next());
196        return list;
197      }
198    
199      List makeList(Pair p) {
200        List list = new ArrayList(1);
201        list.add(p);
202        return list;
203      }
204    
205    }