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 }