View Javadoc

1   /*******************************************************************************
2    * Copyright 2013 André Rouél and Dominik Seichter
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   ******************************************************************************/
16  package net.sf.qualitytest.blueprint;
17  
18  import java.util.Collections;
19  import java.util.HashSet;
20  import java.util.Set;
21  import java.util.Stack;
22  
23  import javax.annotation.Nonnull;
24  import javax.annotation.concurrent.NotThreadSafe;
25  
26  import net.sf.qualitycheck.ArgumentsChecked;
27  import net.sf.qualitycheck.Check;
28  import net.sf.qualitycheck.Throws;
29  import net.sf.qualitycheck.exception.IllegalEmptyArgumentException;
30  import net.sf.qualitycheck.exception.IllegalNullArgumentException;
31  
32  /**
33   * A {@code BlueprintSession} holds information acquired while doing a blueprint of a class. This includes cycle
34   * detection as well as statistical information.
35   * 
36   * @author Dominik Seichter
37   */
38  @NotThreadSafe
39  public final class BlueprintSession {
40  
41  	private static final String SEPARATOR = "->";
42  
43  	private final Stack<Class<?>> stack = new Stack<Class<?>>();
44  	private final Set<Class<?>> classes = new HashSet<Class<?>>();
45  	private int blueprintCount = 0;
46  
47  	private String lastAction = "";
48  
49  	/**
50  	 * Detect cycles in the blueprinting-graph. A cycle occurs when a class is blueprinted within a scope where the same
51  	 * class has been blueprinted before.
52  	 * 
53  	 * @param clazz
54  	 *            a class
55  	 * @return true if a cycle in the blueprinting graph was detected
56  	 */
57  	private boolean detectCycles(@Nonnull final Class<?> clazz) {
58  		return stack.contains(clazz);
59  	}
60  
61  	/**
62  	 * Retrieve all classes that have been blueprinted in the current session.
63  	 * 
64  	 * @return a set of classes encountered while creating the blueprint
65  	 */
66  	public Set<Class<?>> getBlueprintClasses() {
67  		return Collections.unmodifiableSet(classes);
68  	}
69  
70  	/**
71  	 * Retrieve the number of objects which have been blueprinted in the current session.
72  	 * 
73  	 * @return number of objects that have been blueprinted in the current session
74  	 */
75  	public int getBlueprintCount() {
76  		return blueprintCount;
77  	}
78  
79  	/**
80  	 * Get the current blueprinting context as string. This is useful to describe the context in which blueprinting
81  	 * errors have occured.
82  	 * 
83  	 * @return context as string.
84  	 */
85  	public String getContext() {
86  		final StringBuffer buffer = new StringBuffer();
87  		for (int i = 0; i < stack.size(); i++) {
88  			buffer.append(stack.get(i).getName());
89  			if (i < stack.size() - 1) {
90  				buffer.append(SEPARATOR);
91  			}
92  		}
93  
94  		if (!lastAction.isEmpty()) {
95  			buffer.append(" {");
96  			buffer.append(lastAction);
97  			buffer.append('}');
98  		}
99  
100 		return buffer.toString();
101 	}
102 
103 	/**
104 	 * Call after creating a blueprint of a class.
105 	 */
106 	public void pop() {
107 		stack.pop();
108 
109 		blueprintCount++;
110 	}
111 
112 	/**
113 	 * Call before creating a blueprint of a class.
114 	 * 
115 	 * The internal stack is used to do cycle detection in the blueprinting graph.
116 	 * 
117 	 * @param clazz
118 	 *            the class for which a blueprint is created
119 	 * 
120 	 * @return true if a cycle in the blueprinting graph was detected
121 	 * 
122 	 */
123 	@ArgumentsChecked
124 	@Throws(IllegalNullArgumentException.class)
125 	public boolean push(@Nonnull final Class<?> clazz) {
126 		Check.notNull(clazz, "clazz");
127 
128 		final boolean cycle = detectCycles(clazz);
129 
130 		stack.push(clazz);
131 		classes.add(clazz);
132 
133 		return cycle;
134 	}
135 
136 	/**
137 	 * Specify the last action performed on an object. This will be added to the {@link BlueprintSession.getContext()}
138 	 * to allow for simpler debugging.
139 	 * 
140 	 * @param lastAction
141 	 *            A description of the last action.
142 	 */
143 	@ArgumentsChecked
144 	@Throws({ IllegalNullArgumentException.class, IllegalEmptyArgumentException.class })
145 	public void setLastAction(@Nonnull final String lastAction) {
146 		this.lastAction = Check.notEmpty(lastAction, "lastAction");
147 	}
148 }