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 }