1 /***
2 * Copyright 2007 the original author or authors.
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 * For more information visit
11 * http://72miles.com and
12 * http://architecturerules.googlecode.com/svn/docs/index.html
13 */
14
15 package com.seventytwomiles.architecturerules.domain;
16
17
18 import com.seventytwomiles.architecturerules.configuration.ConfigurationFactory;
19 import com.seventytwomiles.architecturerules.exceptions.SourceNotFoundException;
20 import junit.framework.Assert;
21 import org.apache.commons.logging.Log;
22 import org.apache.commons.logging.LogFactory;
23
24 import java.io.File;
25 import java.text.CharacterIterator;
26 import java.text.StringCharacterIterator;
27
28
29
30 /***
31 * <p>Representation of a source directory to search for packages and .class
32 * files in.</p>
33 *
34 * @author mikenereson
35 */
36 public class SourceDirectory {
37
38
39 protected static final Log log = LogFactory.getLog(SourceDirectory.class);
40
41 /***
42 * <p>The value, which is set inside of the xml configuration file, which
43 * indicates that when a source directory is not found, the source directory
44 * should be ignored.</p>
45 *
46 * @parameter NOT_FOUND_IGNORE String
47 */
48 private static final String NOT_FOUND_IGNORE = "ignore";
49
50
51 /***
52 * <p>The value, which is set inside of the xml configuration file, which
53 * indicates that when a source directory is not found, that an exception
54 * should be thrown.</p>
55 *
56 * <p>When {@link #setNotFound(String)} is set to this value, a {@link
57 * SourceNotFoundException} will be thrown.</p>
58 *
59 * @parameter NOT_FOUND_EXCEPTION String
60 */
61 private static final String NOT_FOUND_EXCEPTION = "exception";
62
63 /***
64 * <p>When true, if this source {@link #path} is not found a
65 * <code>SourceNotFoundException</code> will be thrown.</p>
66 *
67 * * <p>If the value is not provided in the configuration, the default value
68 * is used. {@link ConfigurationFactory#DEFAULT_CYCLICAL_DEPENDENCY_CONFIGURATION_VALUE}</p>
69 *
70 * @parameter shouldThrowExceptionWhenNotFound boolean
71 */
72 private boolean shouldThrowExceptionWhenNotFound
73 = ConfigurationFactory.DEFAULT_NO_PACKAGES_CONFIGURATION_BOOLEAN_VALUE;
74
75 /***
76 * <p>Relative url to the path to search in for .class files.</p>
77 *
78 * @parameter path String
79 */
80 private String path;
81
82 /***
83 * <p>Holds the value in the xml configuration for the not-found
84 * property.</p>
85 *
86 * @parameter notFound String
87 * @noinspection UnusedDeclaration
88 */
89 private String notFound;
90
91
92 /***
93 * <p>Instantiates a new SourceDirectory entity.</p>
94 */
95 public SourceDirectory() {
96 }
97
98
99 /***
100 * <p>Instantiates a new SourceDirectory with the given <tt>path</tt></p>
101 *
102 * @param path String {@link #path}
103 */
104 public SourceDirectory(final String path) {
105
106 setPath(path);
107 }
108
109
110 /***
111 * <p>Instantiates a new SourceDirectory with the given <tt>path</tt> and
112 * <tt>shouldThrowExceptionWhenNotFound</tt> values</p>
113 *
114 * @param path String {@link #path}
115 * @param shouldThrowExceptionWhenNotFound boolean {@link
116 * #shouldThrowExceptionWhenNotFound}
117 */
118 public SourceDirectory(final String path,
119 final boolean shouldThrowExceptionWhenNotFound) {
120
121 setShouldThrowExceptionWhenNotFound(shouldThrowExceptionWhenNotFound);
122 setPath(path);
123 }
124
125
126 /***
127 * Setter for property {@link #shouldThrowExceptionWhenNotFound}.
128 *
129 * @param shouldThrowExceptionWhenNotFound Value to set for property
130 * <tt>shouldThrowExceptionWhenNotFound</tt>.
131 */
132 public void setShouldThrowExceptionWhenNotFound(
133 final boolean shouldThrowExceptionWhenNotFound) {
134
135 /***
136 * Update notFound property so that the String value and boolean
137 * values coincide
138 */
139 if (shouldThrowExceptionWhenNotFound) {
140
141 notFound = NOT_FOUND_EXCEPTION;
142
143 } else {
144
145 notFound = NOT_FOUND_IGNORE;
146 }
147
148 this.shouldThrowExceptionWhenNotFound
149 = shouldThrowExceptionWhenNotFound;
150 }
151
152
153 /***
154 * <p>Instantiates a new SourceDirectory with the given <tt>path</tt> and
155 * <tt>notFound</tt> values.</p>
156 *
157 * @param path String {@link #path}
158 * @param notFound boolean {@link @notFound}
159 */
160 public SourceDirectory(final String path, final String notFound) {
161
162 setPath(path);
163 setNotFound(notFound);
164 }
165
166
167 /***
168 * Setter for property 'notFound'.
169 *
170 * @param notFound Value to set for property 'notFound'.
171 */
172 public void setNotFound(String notFound) {
173
174 /***
175 * When null, set to "null" so that the exception message that is about
176 * to be thrown is meaningful.
177 */
178 if (notFound == null)
179 notFound = "null";
180
181 /***
182 * Validate input.
183 * Expect either 'ignore' or 'exception'
184 */
185 if (!(notFound.equalsIgnoreCase(NOT_FOUND_IGNORE) ||
186 notFound.equalsIgnoreCase(NOT_FOUND_EXCEPTION))) {
187
188 throw new IllegalArgumentException(
189 String.format("'not-found' property of '%s' is invalid. " +
190 "valid values are %s and %s",
191 notFound, NOT_FOUND_IGNORE, NOT_FOUND_EXCEPTION));
192 }
193
194 this.notFound = notFound;
195
196 /***
197 * Update shouldThrowExceptionWhenNotFound property so that the
198 * String value and boolean values coincide
199 */
200 final boolean shouldThrowException
201 = getNotFound().equalsIgnoreCase(NOT_FOUND_EXCEPTION);
202
203 setShouldThrowExceptionWhenNotFound(shouldThrowException);
204 }
205
206
207 private String getNotFound() {
208
209 return this.notFound;
210 }
211
212
213 /***
214 * <p>Getter for property {@link #path}.</p>
215 *
216 * @return Value for property <tt>path</tt>.
217 */
218 public String getPath() {
219
220 return this.path;
221 }
222
223
224 /***
225 * <p>Setter for property {@link #path}.</p>
226 *
227 * @param path Value to set for property <tt>path</tt>.
228 */
229 public void setPath(final String path) {
230
231 try {
232
233 Assert.assertNotNull(path);
234 Assert.assertFalse("".equals(path));
235
236 } catch (final Throwable e) {
237
238 throw new IllegalArgumentException(e.getMessage());
239 }
240
241 this.path = replaceBackslashForOS(path);
242 }
243
244
245 /***
246 * <p>Replaces inappropriate backslash with the appropriate slash based on
247 * the operating system's requirements</p>
248 *
249 * <p>For example, on a Windows system, <tt>src/main/resources</tt> becomes
250 * <tt>src//main//resource</tt></p>
251 *
252 * <p>TODO: this may be able to be replaced with String.replaceAll, but I
253 * couldn't get the regex just right</p>
254 *
255 * <p>This todo/issue is open at <a href="http://code.google.com/p/architecturerules/issues/detail?id=29">issue
256 * 29</a></p>
257 *
258 * @param path String the path to fix
259 * @return String the fixed path
260 */
261 String replaceBackslashForOS(final String path) {
262
263 final StringBuffer result = new StringBuffer();
264
265 final StringCharacterIterator iterator
266 = new StringCharacterIterator(path);
267
268 char character = iterator.current();
269
270 final char goal = File.separator.toCharArray()[0];
271 final char target = (goal == '//' ? '/' : '//');
272
273 while (character != CharacterIterator.DONE) {
274
275 result.append(character == target ? goal : character);
276 character = iterator.next();
277 }
278
279 return result.toString();
280 }
281
282
283 /***
284 * @see Object#equals(Object)
285 */
286 @Override
287 public boolean equals(final Object object) {
288
289 if (this == object)
290 return true;
291
292 if (object == null)
293 return false;
294
295 if (!(object instanceof SourceDirectory))
296 return false;
297
298 final SourceDirectory that = (SourceDirectory) object;
299
300 if (path != null
301 ? !path.equals(that.getPath())
302 : that.getPath() != null) {
303
304 return false;
305 }
306
307 return true;
308 }
309
310
311 /***
312 * @see Object#hashCode()
313 */
314 @Override
315 public int hashCode() {
316
317 return (this.path != null ? this.path.hashCode() : 0);
318 }
319
320
321 /***
322 * @see Object#toString()
323 */
324 @Override
325 public String toString() {
326
327 return this.path;
328 }
329
330
331 /***
332 * Getter for property {@link #shouldThrowExceptionWhenNotFound}.
333 *
334 * @return Value for property <tt>shouldThrowExceptionWhenNotFound</tt>.
335 */
336 public boolean shouldThrowExceptionWhenNotFound() {
337
338 return this.shouldThrowExceptionWhenNotFound;
339 }
340 }