View Javadoc

1   /**
2    * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3    */
4   package net.sourceforge.pmd.lang.dfa.report;
5   
6   import java.util.Iterator;
7   
8   import net.sourceforge.pmd.RuleViolation;
9   
10  public class ReportTree implements Iterable<RuleViolation> {
11  
12  	private PackageNode rootNode = new PackageNode("");
13  	private AbstractReportNode level;
14  
15  	private class TreeIterator implements Iterator<RuleViolation> {
16  
17  		private AbstractReportNode iterNode = rootNode;
18  		private boolean hasNextFlag;
19  
20  		public void remove() {
21  			throw new UnsupportedOperationException();
22  		}
23  
24  		public boolean hasNext() {
25  			hasNextFlag = true;
26  			return getNext() != null;
27  		}
28  
29  		public RuleViolation next() {
30  			if (!hasNextFlag) {
31  				getNext();
32  			} else {
33  				hasNextFlag = false;
34  			}
35  
36  			if (iterNode instanceof ViolationNode) {
37  				return ((ViolationNode) iterNode).getRuleViolation();
38  			}
39  			return null;
40  		}
41  
42  		/**
43  		 * It's some kind of left-right-middle search (postorder). It always
44  		 * returns only leafs. The first node he returns is the most left handed
45  		 * leaf he can found. Now he's looking for siblings and if there are
46  		 * any, he starts searching for the next most left handed leaf. If there
47  		 * are no siblings he goes up to his parent and starts looking for
48  		 * siblings. If there are any he starts searching for the next most left
49  		 * handed leaf again. And so on ... until he wants to get the parent of
50  		 * the root node. Because there is no one, the search stops.
51  		 */
52  
53  		private AbstractReportNode getNext() {
54  			AbstractReportNode node;
55  
56  			while (true) {
57  				if (iterNode.isLeaf()) {
58  
59  					while ((node = iterNode.getNextSibling()) == null) {
60  
61  						node = iterNode.getParent();
62  						if (node == null) {
63  							return null;
64  						} else {
65  							iterNode = node;
66  						}
67  					}
68  
69  					iterNode = node;
70  					if (iterNode.isLeaf()) {
71  						return iterNode;
72  					} else {
73  						continue;
74  					}
75  				} else {
76  					iterNode = iterNode.getFirstChild();
77  					if (iterNode.isLeaf()) {
78  						return iterNode;
79  					} else {
80  						continue;
81  					}
82  				}
83  			}
84  		}
85  	}
86  
87  	@Override
88  	public Iterator<RuleViolation> iterator() {
89  		return new TreeIterator();
90  	}
91  
92  	public int size() {
93  		int count = 0;
94  		for (Iterator<RuleViolation> i = iterator(); i.hasNext();) {
95  			i.next();
96  			count++;
97  		}
98  		return count;
99  	}
100 
101 	public AbstractReportNode getRootNode() {
102 		return rootNode;
103 	}
104 
105 	/**
106 	 * Adds the RuleViolation to the tree. Splits the package name. Each
107 	 * package, class and violation gets there own tree node.
108 	 */
109 	public void addRuleViolation(RuleViolation violation) {
110 		String packageName = violation.getPackageName();
111 		if (packageName == null) {
112 			packageName = "";
113 		}
114 
115 		level = rootNode;
116 
117 		int endIndex = packageName.indexOf('.');
118 		while (true) {
119 			String parentPackage;
120 			if (endIndex < 0) {
121 				parentPackage = packageName;
122 			} else {
123 				parentPackage = packageName.substring(0, endIndex);
124 			}
125 
126 			if (!isStringInLevel(parentPackage)) {
127 				PackageNode node = new PackageNode(parentPackage);
128 				level.addFirst(node);
129 				// gotoLevel
130 				level = node;
131 			}
132 
133 			if (endIndex < 0) {
134 				break;
135 			}
136 			endIndex = packageName.indexOf('.', endIndex + 1);
137 		}
138 
139 		String cl = violation.getClassName();
140 
141 		if (!isStringInLevel(cl)) {
142 			ClassNode node = new ClassNode(cl);
143 			level.addFirst(node);
144 			// gotoLevel
145 			level = node;
146 		}
147 
148 		/*
149 		 * Filters duplicated rule violations. Like the comparator in
150 		 * RuleViolation if he already exists.
151 		 */
152 		ViolationNode tmp = new ViolationNode(violation);
153 		if (!equalsNodeInLevel(level, tmp)) {
154 			level.add(tmp);
155 		}
156 	}
157 
158 	/**
159 	 * Checks if node is a child of the level node.
160 	 */
161 	private boolean equalsNodeInLevel(AbstractReportNode level,
162 			AbstractReportNode node) {
163 		for (int i = 0; i < level.getChildCount(); i++) {
164 			if (level.getChildAt(i).equalsNode(node)) {
165 				return true;
166 			}
167 		}
168 		return false;
169 	}
170 
171 	/**
172 	 * Checks if the packageName or the className is a child of the current
173 	 * (this.level) node. If it's true, the current node changes to the child
174 	 * node.
175 	 */
176 	private boolean isStringInLevel(String str) {
177 
178 		for (int i = 0; i < level.getChildCount(); i++) {
179 			final AbstractReportNode child = level.getChildAt(i);
180 			final String tmp;
181 			if (child instanceof PackageNode) {
182 				tmp = ((PackageNode) child).getPackageName();
183 			} else if (child instanceof ClassNode) {
184 				tmp = ((ClassNode) child).getClassName();
185 			} else {
186 				return false;
187 			}
188 
189 			if (tmp != null && tmp.equals(str)) {
190 				// goto level
191 				level = child;
192 				return true;
193 			}
194 		}
195 		return false;
196 	}
197 
198 }