001 package edu.rice.cs.cunit.threadCheck;
002
003 import edu.rice.cs.cunit.classFile.ClassFile;
004 import edu.rice.cs.cunit.classFile.attributes.AAnnotationsAttributeInfo;
005 import edu.rice.cs.cunit.instrumentors.threadCheck.*;
006 import static edu.rice.cs.cunit.instrumentors.threadCheck.AThreadCheckStrategy.*;
007 import edu.rice.cs.cunit.util.Debug;
008 import edu.rice.cs.cunit.util.ILambda;
009 import edu.rice.cs.cunit.util.IPredicate;
010 import edu.rice.cs.cunit.util.XMLConfig;
011 import org.w3c.dom.Node;
012
013 import java.io.StringReader;
014 import java.util.ArrayList;
015 import java.util.HashMap;
016 import java.util.List;
017
018 /**
019 * Utilities to write annotations to an XML configuration.
020 *
021 * @author Mathias Ricken
022 */
023 public class XMLAnnotUtils {
024 /**
025 * Utilities for putting annotations into an XML configuration.
026 */
027 public static class AnnotationToXML {
028 /**
029 * Put the annotations from the lists into the XML document.
030 *
031 * @param xc XML configuration
032 * @param root node under which they are to appear
033 * @param tcAR records of annotations
034 */
035 public static void processAnnotations(XMLConfig xc,
036 Node root,
037 ThreadCheckAnnotationRecord tcAR) {
038 for(String s : tcAR.denyThreadNames) {
039 Node node = xc.set("name.type", "not", root, false);
040 xc.set(".value", s, node, false);
041 }
042 for(String s : tcAR.denyThreadGroups) {
043 Node node = xc.set("group.type", "not", root, false);
044 xc.set(".value", s, node, false);
045 }
046 for(Long id : tcAR.denyThreadIds) {
047 Node node = xc.set("id.type", "not", root, false);
048 xc.set(".value", String.valueOf(id), node, false);
049 }
050 for(String s : tcAR.allowThreadNames) {
051 Node node = xc.set("name.type", "only", root, false);
052 xc.set(".value", s, node, false);
053 }
054 for(String s : tcAR.allowThreadGroups) {
055 Node node = xc.set("group.type", "only", root, false);
056 xc.set(".value", s, node, false);
057 }
058 for(Long id : tcAR.allowThreadIds) {
059 Node node = xc.set("id.type", "only", root, false);
060 xc.set(".value", String.valueOf(id), node, false);
061 }
062 if (tcAR.allowEventThread != OnlyRunBy.EVENT_THREAD.NO) {
063 xc.set("eventThread.type", tcAR.allowEventThread.toString().toLowerCase(), root, false);
064 }
065 HashMap<String, ArrayList<PredicateAnnotationRecord>> rootSets
066 = new HashMap<String, ArrayList<PredicateAnnotationRecord>>();
067 rootSets.put("", tcAR.predicateAnnotations);
068 processPredicateAnnotations(xc, root, rootSets);
069 }
070
071 /**
072 * Process a list of predicate annotations and add <predicate> or <combine> tags underneath the specified node.
073 *
074 * @param xc XML configuration
075 * @param node XML node that is going to be the parent of the <predicate> or <combine> tags
076 * @param pars predicate annotation records
077 */
078 private static void processPredicateAnnotations(XMLConfig xc,
079 Node node,
080 HashMap<String, ArrayList<PredicateAnnotationRecord>> pars) {
081 for(final String key : pars.keySet()) {
082 for(final PredicateAnnotationRecord par : pars.get(key)) {
083 Node predNode;
084 if (par.predicateClass != null) {
085 // @PredicateLink specified
086 predNode = xc.set("predicate.type", par.annotation.getType(), node, false);
087 Node membersNode = predNode.getOwnerDocument().createElement("values");
088 predNode.appendChild(membersNode);
089 processPredicateMembers(xc, membersNode, par.paramNames, par.paramTypes, par.valueList);
090 }
091 else {
092 // @Combine specified
093 predNode = xc.set("combine.type", par.annotation.getType(), node, false);
094 Node predicatesNode = xc.set("values.mode", par.combineMode.toString(), predNode, false);
095 processPredicateAnnotations(xc, predicatesNode, par.combinedPredicates);
096 }
097 if (par.passArguments) {
098 // method arguments should be passed
099 xc.set(".arguments", "true", predNode, true);
100 }
101 processAnnotation(xc, predNode, par.annotation.getPairs(), par.paramTypes);
102 }
103 }
104 }
105
106 /**
107 * Process an annotation and add the <arg> tags underneath the specified node.
108 *
109 * @param xc XML configuration
110 * @param node XML node that is going to be the parent of the <arg> tags
111 * @param nvpList list of name-value pairs
112 * @param paramTypes list of parameter types, or null if this information is not available
113 */
114 private static void processAnnotation(final XMLConfig xc,
115 Node node,
116 List<AAnnotationsAttributeInfo.Annotation.NameValuePair> nvpList,
117 final HashMap<String, String> paramTypes) {
118 for(final AAnnotationsAttributeInfo.Annotation.NameValuePair nvp : nvpList) {
119 Node argNode = xc.set("arg.name", nvp.getName().toString(), node, false);
120 nvp.getValue()
121 .execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Node>() {
122 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host,
123 Node argNode) {
124 xc.set(".type", String.valueOf(host.getTag()), argNode, false);
125 xc.set(".value", host.toString(), argNode, false);
126 return null;
127 }
128
129 public Object arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host,
130 Node argNode) {
131 xc.set(".type", String.valueOf(host.getTag()), argNode, false);
132 if ((paramTypes != null) && (paramTypes.size() > 0)) {
133 final String type = paramTypes.get(nvp.getName().toString());
134 xc.set(".desc", type, argNode, false); // in case the array is empty, we need a type
135 }
136 xc.set(".value", String.valueOf(host.getEntries().length), argNode, false);
137 for(AAnnotationsAttributeInfo.Annotation.AMemberValue mv : host.getEntries()) {
138 Node elNode = argNode.getOwnerDocument().createElement("element");
139 argNode.appendChild(elNode);
140 mv.execute(this, elNode);
141 }
142 return null;
143 }
144
145 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host,
146 Node argNode) {
147 xc.set(".type", String.valueOf(host.getTag()), argNode, false);
148 xc.set(".value", String.valueOf(host.getAnnotation().getType()), argNode, false);
149
150 processAnnotation(xc, argNode, host.getAnnotation().getPairs(), null);
151 return null;
152 }
153 }, argNode);
154 }
155 }
156
157 /**
158 * Process a list of members and add the <arg> tags underneath the specified node.
159 *
160 * @param xc XML configuration
161 * @param node XML node that is going to be the parent of the <arg> tags
162 * @param paramNames list of parameter names
163 * @param paramTypes list of parameter types, or null if that information is not available
164 * @param valueList list of values
165 */
166 private static void processPredicateMembers(final XMLConfig xc,
167 Node node,
168 List<String> paramNames,
169 final HashMap<String, String> paramTypes,
170 List<AAnnotationsAttributeInfo.Annotation.AMemberValue> valueList) {
171 for(int i = 0; i < paramNames.size(); ++i) {
172 final String name = paramNames.get(i);
173 AAnnotationsAttributeInfo.Annotation.AMemberValue value = valueList.get(i);
174 Node argNode = xc.set("arg.name", name, node, false);
175 value.execute(new AAnnotationsAttributeInfo.Annotation.ADefaultMemberValueVisitor<Object, Node>() {
176 public Object defaultCase(AAnnotationsAttributeInfo.Annotation.AMemberValue host, Node argNode) {
177 xc.set(".type", String.valueOf(host.getTag()), argNode, false);
178 xc.set(".value", host.toString(), argNode, false);
179 return null;
180 }
181
182 public Object arrayMemberCase(AAnnotationsAttributeInfo.Annotation.ArrayMemberValue host,
183 Node argNode) {
184 xc.set(".type", String.valueOf(host.getTag()), argNode, false);
185 if ((paramTypes != null) && (paramTypes.size() > 0)) {
186 final String type = paramTypes.get(name);
187 xc.set(".desc", type, argNode, false); // in case the array is empty, we need a type
188 }
189 xc.set(".value", String.valueOf(host.getEntries().length), argNode, false);
190 for(AAnnotationsAttributeInfo.Annotation.AMemberValue mv : host.getEntries()) {
191 Node elNode = argNode.getOwnerDocument().createElement("element");
192 argNode.appendChild(elNode);
193 mv.execute(this, elNode);
194 }
195 return null;
196 }
197
198 public Object annotationMemberCase(AAnnotationsAttributeInfo.Annotation.AnnotationMemberValue host,
199 Node argNode) {
200 xc.set(".type", String.valueOf(host.getTag()), argNode, false);
201 xc.set(".value", String.valueOf(host.getAnnotation().getType()), argNode, false);
202
203 processAnnotation(xc, argNode, host.getAnnotation().getPairs(), null);
204 return null;
205 }
206 }, argNode);
207 }
208 }
209 }
210
211 /**
212 * Class that maintains a map between invariants and definitions.
213 */
214 public static class ConcurrentyDefinitions extends HashMap<ThreadCheckAnnotationRecord, ThreadCheckDefinitionRecord> {
215 // nothing additional required, just to shorten the writing
216 }
217
218 /**
219 * Definition of DEFALT_XML_CONC_DEF_PATH_PREFIX without the trailing '/'
220 */
221 private static final String CD_PREFIX_NOSLASH =
222 AThreadCheckStrategy.DEFAULT_XML_CONC_DEF_PATH_PREFIX.substring(0, AThreadCheckStrategy.DEFAULT_XML_CONC_DEF_PATH_PREFIX.length() - 1);
223
224 /**
225 * Convert XML concurrency definitions into a map between invariants and definitions.
226 * This works for both <threadcheck> and <threadcheck:def>.
227 * @param xc new XML concurrency definitions
228 * @return the map between invariants and definitions
229 */
230 public static ConcurrentyDefinitions convertXMLToConcDefs(XMLConfig xc) {
231 return joinXMLConcDefs(xc, new ConcurrentyDefinitions());
232 }
233
234 /**
235 * Join the two XML concurrency definitions
236 * This works for both <threadcheck> and <threadcheck:def>.
237 * @param concDef1 first XML file
238 * @param concDef2 second XML file
239 * @return joined XML concurrency definitions
240 */
241 public static XMLConfig joinXMLConcDefs(XMLConfig concDef1, XMLConfig concDef2) {
242 ConcurrentyDefinitions defsAccum = convertXMLToConcDefs(concDef1);
243 defsAccum = joinXMLConcDefs(concDef2, defsAccum);
244 return convertConcDefsToConcDefBasedXML(defsAccum);
245 }
246
247 /**
248 * Convert the map between invariants and definitions back to XML concurrency definitions (<threadcheck:def>).
249 * @param defsAccum accumulated map
250 * @return XML concurrency definition configuration (<threadcheck:def>)
251 */
252 public static XMLConfig convertConcDefsToConcDefBasedXML(ConcurrentyDefinitions defsAccum) {
253 XMLConfig xc = createEmptyXMLConcDefs();
254 for(ThreadCheckDefinitionRecord cd: defsAccum.values()) {
255 Node defNode = xc.createNode(CD_PREFIX_NOSLASH, null, false);
256 Node invNode = xc.createNode("invariant", defNode);
257 AnnotationToXML.processAnnotations(xc, invNode, cd.getInvariant());
258 if (cd.classNames.size()>0) {
259 for(String s: cd.classNames) {
260 Node classNode = xc.createNode("class", defNode, false);
261 xc.set(".name", s, classNode, true);
262 }
263 }
264 if (cd.methodClassAndSigs.size()>0) {
265 for(String s: cd.methodClassAndSigs.keySet()) {
266 int sepPos = s.indexOf(AThreadCheckStrategy.CLASS_SIG_SEPARATOR_STRING);
267 if (sepPos<0) {
268 throw new ThreadCheckException("Method description of <class>::<signature> was incorrect");
269 }
270 String className = s.substring(0, sepPos);
271 String sig = s.substring(sepPos + AThreadCheckStrategy.CLASS_SIG_SEPARATOR_STRING.length());
272 Node methodNode = xc.createNode("method", defNode, false);
273 xc.set(".name", className, methodNode, true);
274 xc.set(".sig", sig, methodNode, true);
275 if (cd.methodClassAndSigs.get(s)) {
276 xc.set(".suppress", "true", methodNode, true);
277 }
278 }
279 }
280 }
281 return xc;
282 }
283
284 /**
285 * Convert the map between invariants and definitions back to a class-based XML configuration (<threadcheck>).
286 * @param defsAccum accumulated map
287 * @return class-based XML configuration (<threadcheck>)
288 */
289 public static XMLConfig convertConcDefsToClassBasedXML(ConcurrentyDefinitions defsAccum) {
290 XMLConfig xc = new XMLConfig(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
291 "<concutest>\n"+
292 " <threadcheck>"+
293 " </threadcheck>"+
294 "</concutest>\n"));
295
296 List<Node> roots = xc.getNodes(AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX);
297 if (roots.size()!=1) {
298 throw new ThreadCheckException("The output XML file must contain exactly one node with the path "+
299 AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX+", found "+roots.size());
300 }
301 Node tcRoot = roots.get(0);
302 for(ThreadCheckDefinitionRecord cd: defsAccum.values()) {
303 if (cd.classNames.size()>0) {
304 for(String s: cd.classNames) {
305 // convert '$' from inner classes to '-' because XML does not allow '$' inside node names
306 String classPath = s.replace('.', '/').replace('$', '-')+"/class";
307 List<Node> cNodes = xc.getNodes(classPath, tcRoot);
308 if (cNodes.size()>1) {
309 throw new ThreadCheckException("The output XML file must contain exactly one node with the path "+
310 AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX+classPath+
311 ", found "+cNodes.size());
312 }
313 Node classNode;
314 if (cNodes.size()==1) {
315 // already exists, reuse it
316 classNode = cNodes.get(0);
317 }
318 else {
319 // create a new one
320 classNode = xc.createNode(classPath, tcRoot);
321 }
322 // add the class invariant
323 XMLAnnotUtils.AnnotationToXML.processAnnotations(xc, classNode, cd.getInvariant());
324 }
325 }
326 if (cd.methodClassAndSigs.size()>0) {
327 for(String s: cd.methodClassAndSigs.keySet()) {
328 int sepPos = s.indexOf(AThreadCheckStrategy.CLASS_SIG_SEPARATOR_STRING);
329 if (sepPos<0) {
330 throw new ThreadCheckException("Method description of <class>::<signature> was incorrect");
331 }
332 // convert '$' from inner classes to '-' because XML does not allow '$' inside node names
333 String classPath = s.substring(0, sepPos).replace('.', '/').replace('$', '-')+"/class";
334 String sig = s.substring(sepPos + AThreadCheckStrategy.CLASS_SIG_SEPARATOR_STRING.length());
335 // find or create the class node
336 List<Node> cNodes = xc.getNodes(classPath, tcRoot);
337 if (cNodes.size()>1) {
338 throw new ThreadCheckException("The output XML file must contain exactly one node with the path "+
339 AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX+classPath+
340 ", found "+cNodes.size());
341 }
342 Node classNode;
343 if (cNodes.size()==1) {
344 // already exists, reuse it
345 classNode = cNodes.get(0);
346 }
347 else {
348 // create a new one
349 classNode = xc.createNode(classPath, tcRoot);
350 }
351 // find or create the method node
352 List<Node> mNodes = xc.getNodes("method", classNode);
353 Node methodNode = null;
354 for(Node mn: mNodes) {
355 try {
356 List<String> sigAttrs = xc.getMultiple(".sig", mn);
357 if (sigAttrs.size()!=1) {
358 throw new ThreadCheckException("The output XML file must contain a sig attribute for the path "+
359 AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX+classPath+"/method");
360 }
361 if (sig.equals(sigAttrs.get(0))) {
362 methodNode = mn;
363 break;
364 }
365 }
366 catch(XMLConfig.XMLConfigException e) {
367 if (!e.equals(new XMLConfig.XMLConfigException("Node method has no attribute with name sig"))) {
368 throw new ThreadCheckException("The output XML file must contain a sig attribute for the path "+
369 AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX+classPath+"/method");
370 }
371 }
372 }
373 if (methodNode==null) {
374 // create a new one
375 methodNode = xc.createNode("method", classNode, false);
376 xc.set(".sig", sig, methodNode, true);
377 if (cd.methodClassAndSigs.get(s)) {
378 xc.set(".suppress", "true", methodNode, true);
379 }
380 }
381
382 // add the method invariant
383 XMLAnnotUtils.AnnotationToXML.processAnnotations(xc, methodNode, cd.getInvariant());
384 }
385 }
386 }
387 return xc;
388 }
389
390 /**
391 * Add the XML concurrency definitions in xc to the ones already in the accumulator.
392 * This works for both <threadcheck> and <threadcheck:def>.
393 * @param xc new XML concurrency definitions
394 * @param defsAccum accumulator of all the invariants and definitions
395 * @return the accumulator, i.e. defsAccum
396 */
397 public static ConcurrentyDefinitions joinXMLConcDefs(XMLConfig xc, ConcurrentyDefinitions defsAccum) {
398 AAddThreadCheckStrategy dummy = createDummy();
399 // go through the concurrency definitions
400 List<ThreadCheckDefinitionRecord> cds = dummy.extractXMLConcDef(xc);
401 for(ThreadCheckDefinitionRecord cd : cds) {
402 // if we should join definitions with equal invariants, we need to check the map first
403 ThreadCheckDefinitionRecord old = defsAccum.get(cd.getInvariant());
404 if (old != null) {
405 // invariant already exists, join
406 for(String s : old.classNames) {
407 cd.addClass(s);
408 }
409 for(String s : old.methodClassAndSigs.keySet()) {
410 int sepPos = s.indexOf(AThreadCheckStrategy.CLASS_SIG_SEPARATOR_STRING);
411 if (sepPos < 0) {
412 throw new ThreadCheckException("Method description of <class>::<signature> was incorrect");
413 }
414 cd.addMethod(s.substring(0, sepPos),
415 s.substring(sepPos + AThreadCheckStrategy.CLASS_SIG_SEPARATOR_STRING.length()),
416 old.methodClassAndSigs.get(s));
417 }
418 }
419 defsAccum.put(cd.getInvariant(), cd);
420 }
421
422 return defsAccum;
423 }
424
425 /**
426 * Convert an XML thread checker configuration to a class-based representation (<threadcheck>).
427 * The input XML configuration can be mixed between <threadcheck> and <threadcheck:def>.
428 * @param xc input XML thread checker configuration
429 * @return class-based XML thread checker configuration (<threadcheck>)
430 */
431 public static XMLConfig convertXMLToClassBasedXML(XMLConfig xc) {
432 ConcurrentyDefinitions defsAccum = convertXMLToConcDefs(xc);
433 return convertConcDefsToClassBasedXML(defsAccum);
434 }
435
436 /**
437 * Convert an XML thread checker configuration to an XML concurrency definition configuration (<threadcheck:def>).
438 * The input XML configuration can be mixed between <threadcheck> and <threadcheck:def>.
439 * @param xc input XML thread checker configuration
440 * @return XML concurrency definition configuration (<threadcheck:def>)
441 */
442 public static XMLConfig convertXMLToConcDefXML(XMLConfig xc) {
443 ConcurrentyDefinitions defsAccum = convertXMLToConcDefs(xc);
444 return convertConcDefsToConcDefBasedXML(defsAccum);
445 }
446
447 /**
448 * Creates enough of a dummy AAddThreadCheckStrategy to let us do our work.
449 * @return dummy AAddThreadCheckStrategy with limited capabilities
450 */
451 public static AAddThreadCheckStrategy createDummy() {
452 boolean debug = Debug.out.isDebug();
453 Debug.out.setDebug(false);
454 List<String> parameters = new ArrayList<String>();
455 SharedData sharedData = new SharedData(parameters);
456 Debug.out.setDebug(debug);
457 return new AAddThreadCheckStrategy(sharedData,
458 new AAddThreadCheckStrategy.SharedAddData(parameters, sharedData)) {
459 public void instrument(ClassFile cf) {
460 // no, I'm not doing that here
461 }
462 public void done() {
463 // nothing to do
464 }
465 };
466 }
467
468 /**
469 * Create an empty set of XML concurrency definitions.
470 * @return empty set of XML concurrency definitions
471 */
472 public static XMLConfig createEmptyXMLConcDefs() {
473 return new XMLConfig(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
474 "<concutest>\n"+
475 "</concutest>\n"));
476 }
477
478 /**
479 * Create an empty set of XML annotations.
480 * @return empty set of XML annotations
481 */
482 public static XMLConfig createEmptyXMLAnnotaations() {
483 return new XMLConfig(new StringReader("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"+
484 "<concutest>\n"+
485 " <threadcheck>"+
486 " </threadcheck>"+
487 "</concutest>\n"));
488 }
489
490 /**
491 * Utilities for working with class-based thread checker definitions (<threadcheck>).
492 */
493 public static class ClassBased {
494 /**
495 * Definition of DEFALT_XML_PATH_PREFIX without the trailing '/'
496 */
497 private static final String PATH_PREFIX =
498 AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX.substring(0, AThreadCheckStrategy.DEFAULT_XML_PATH_PREFIX.length() - 1);
499
500 /**
501 * Process a node.
502 * @param xc the XML configuration
503 * @param classLambda the lambda to be executed when a class has been processed completely
504 * @param processClassPred a predicate that determines whether a class should be processed, based on the class name
505 * @param exList a list of exceptions generated while processing the node
506 */
507 public static void processNode(XMLConfig xc,
508 ILambda.Ternary<
509 Object,
510 String,
511 ThreadCheckAnnotationRecord,
512 HashMap<String,ThreadCheckAnnotationRecord>> classLambda,
513 IPredicate<String> processClassPred,
514 List<ThreadCheckException> exList) {
515 List<Node> nodes = xc.getNodes(PATH_PREFIX+"/*");
516 if (nodes!=null) {
517 for(Node n: nodes) {
518 processNode(xc, classLambda, n, "", processClassPred, exList);
519 }
520 }
521 }
522
523 /**
524 * Process a node.
525 * @param xc the XML configuration
526 * @param classLambda the lambda to be executed when a class has been processed completely
527 * @param n the node
528 * @param path the path, not including the node itself
529 * @param processClassPred a predicate that determines whether a class should be processed, based on the class name
530 * @param exList a list of exceptions generated while processing the node
531 */
532 private static void processNode(XMLConfig xc,
533 ILambda.Ternary<
534 Object,
535 String,
536 ThreadCheckAnnotationRecord,
537 HashMap<String,ThreadCheckAnnotationRecord>> classLambda,
538 Node n,
539 String path,
540 IPredicate<String> processClassPred,
541 List<ThreadCheckException> exList) {
542 final AAddThreadCheckStrategy dummy = createDummy();
543 final String className = (path+"/"+n.getNodeName()).replace('/','.').replace('-','$').substring(1);
544 final List<Node> allNodes = xc.getNodes(PATH_PREFIX+path+"/"+n.getNodeName()+"/*");
545 for(Node an: allNodes) {
546 if (!an.getNodeName().equals("class")) {
547 // recur
548 processNode(xc, classLambda, an, path+"/"+n.getNodeName(), processClassPred, exList);
549 }
550 else {
551 final List<Node> classNodes = xc.getNodes(PATH_PREFIX+path+"/"+n.getNodeName()+"/class");
552 if (classNodes.size()>1) {
553 exList.add(new ThreadCheckException("Improper format, "+path+"/"+n.getNodeName()+" has multiple <class> tags"));
554 continue;
555 }
556 for(Node cn: classNodes) {
557 processClassNode(xc, classLambda, cn, path+"/"+n.getNodeName(), className, processClassPred, exList, dummy);
558 }
559 }
560 }
561 }
562
563 /**
564 * Process a <class> node
565 * @param xc the XML configuration
566 * @param classLambda the lambda to be executed when a class has been processed completely
567 * @param n the class node
568 * @param path the path, not including the class node itself
569 * @param className name of the class
570 * @param processClassPred a predicate that determines whether a class should be processed, based on the class name
571 * @param exList a list of exceptions generated while processing the node
572 * @param dummy dummy AAddThreadCheckStrategy
573 */
574 private static void processClassNode(XMLConfig xc,
575 ILambda.Ternary<
576 Object,
577 String,
578 ThreadCheckAnnotationRecord,
579 HashMap<String,ThreadCheckAnnotationRecord>> classLambda,
580 Node n,
581 String path,
582 String className,
583 IPredicate<String> processClassPred,
584 List<ThreadCheckException> exList,
585 AAddThreadCheckStrategy dummy) {
586 if (!processClassPred.apply(className)) {
587 return;
588 }
589 final ThreadCheckAnnotationRecord classAnnots = dummy.extractXMLAnnotations(n);
590 // key = method signature
591 final HashMap<String,ThreadCheckAnnotationRecord> methodAnnots = new HashMap<String,ThreadCheckAnnotationRecord>();
592 final List<Node> methodNodes = xc.getNodes(PATH_PREFIX+path+"/"+n.getNodeName()+"/method");
593 for(Node mn: methodNodes) {
594 final Node sigAttr = mn.getAttributes().getNamedItem("sig");
595 if (sigAttr==null) {
596 exList.add(new ThreadCheckException("Improper format, "+path+"/"+n.getNodeName()+"/class has a <method> tag without sig attribute"));
597 continue;
598 }
599 final String sig = sigAttr.getNodeValue();
600 ThreadCheckAnnotationRecord tcar = dummy.extractXMLAnnotations(mn);
601 final Node suppressAttr = mn.getAttributes().getNamedItem("suppress");
602 if ((suppressAttr!=null) && (suppressAttr.getNodeValue().equalsIgnoreCase("true"))) {
603 tcar.suppressSubtypingWarning = true;
604 }
605 methodAnnots.put(sig, tcar);
606 }
607 if ((!classAnnots.empty()) || (methodAnnots.size()>0)) {
608 classLambda.apply(className,classAnnots,methodAnnots);
609 }
610 }
611 }
612 }