View Javadoc

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 org.apache.commons.logging.Log;
19  import org.apache.commons.logging.LogFactory;
20  
21  import static java.lang.String.format;
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  
26  
27  /***
28   * <p>A java package. This class wraps the java package to give it
29   * functionality, such as the ability to check and see if it matches another
30   * package.</p>
31   *
32   * @author mikenereson
33   */
34  public class JPackage {
35  
36  
37      private static final Log log
38              = LogFactory.getLog(JPackage.class);
39  
40      /***
41       * <p>All of the symbols or characters that represent a wildcard.</p>
42       */
43      private static final char[] WILDCHARS = new char[]{'*'};
44  
45      /***
46       * <p>period separated path to package such as <samp>com.seventeytwomiles.architecturerules.domain</samp>.</p>
47       *
48       * @parameter path String
49       */
50      private String path;
51  
52  
53      /***
54       * <p>Constructs a new <code>JPackage</code></p>
55       */
56      public JPackage() {
57      }
58  
59  
60      /***
61       * <p>Constructs a new <code>JPackage</code> with the given
62       * <tt>path</tt></p>
63       *
64       * @param path String to set for {@link #path}
65       */
66      public JPackage(final String path) {
67  
68          setPath(path);
69      }
70  
71  
72      /***
73       * <p>Getter for property {@link #path}.</p>
74       *
75       * @return Value for property <tt>path</tt>.
76       */
77      public String getPath() {
78  
79          return path;
80      }
81  
82  
83      /***
84       * <p>Setter for property  {@link #path}</p>
85       *
86       * @param path Value to set for property <tt>path</tt>
87       */
88      public void setPath(final String path) {
89  
90          this.path = path;
91      }
92  
93  
94      public boolean equals(final Object o) {
95  
96          if (this == o)
97              return true;
98  
99          if (!(o instanceof JPackage))
100             return false;
101 
102         final JPackage that = (JPackage) o;
103 
104         if (path != null
105                 ? !path.equals(that.getPath())
106                 : that.getPath() != null) {
107 
108             return false;
109         }
110 
111         return true;
112     }
113 
114 
115     public int hashCode() {
116 
117         return (path != null ? path.hashCode() : 0);
118     }
119 
120 
121     /***
122      * @see Object#toString()
123      */
124     public String toString() {
125 
126         return this.path;
127     }
128 
129 
130     /***
131      * <p>Determines if a given <code>JPackage</code> or <code>String</code> is
132      * represented by this <code>JPackage</code>.</p>
133      *
134      * <p>If given Object is empty String then <tt>false<tt><</p>
135      *
136      * @param that a String or JPackage
137      * @return boolean <tt>true</tt> when a perfect match is found or when the
138      *         wildcards match.
139      */
140     public boolean matches(final Object that) {
141 
142         if (!(that instanceof String)
143                 && !(that instanceof JPackage)) {
144 
145             return false;
146         }
147 
148         if (that.equals(""))
149             return false;
150 
151 
152         if (hasWildcards()) {
153 
154             return regExMatch(that);
155 
156         } else {
157 
158             return prefectMatch(that);
159         }
160     }
161 
162 
163     /***
164      * <p>Determines if this <code>JPackage</code> uses wildcards to match more
165      * than one package.</p>
166      *
167      * @return boolean <tt>true</tt> when <tt>path</tt> contains any of the
168      *         {@link #WILDCHARS}.
169      */
170     private boolean hasWildcards() {
171 
172         for (final char wildChar : WILDCHARS)
173             if (this.path.contains(String.valueOf(wildChar)))
174                 return true;
175 
176         return false;
177     }
178 
179 
180     /***
181      * <p>Manipulates the <tt>path</tt> value to add Regular Expression support
182      * then attempts to match the Reg Ex against the given <tt>Object</tt>.</p>
183      *
184      * <p>This supports <dl> <dt>terminating package</dt> <dd>1.2.*</dd>
185      * <dt>terminating package or sub package description</dt> <dd>1.2..*</dd>
186      * <dt>internal package</dt> <dd>1.*.2</dd> <dt>internal package or sub
187      * package</dt> <dd>1.*..4</dd> <dt>internal and terminating</dt>
188      * <dd>1.*.3.*</dd> <dd>1.*.3..*</dd><dd>1..*.5.*</dd> </dl></p>
189      *
190      *
191      * <p>TODO: This does not support the single character <tt>*</tt> yet.</p>
192      *
193      * @param that <code>Object</code> of type <code>String</code> or
194      * <code>JPackage</code>
195      * @return boolean <tt>true</tt> when the given <tt>Object</tt> is a
196      *         supported type, and then regular expression that is constructed
197      *         matches.
198      */
199     private boolean regExMatch(final Object that) {
200 
201         /***
202          * TODO: code support for path = "*"
203          * TODO: then Update javadoc
204          */
205         final String regex = this.path
206                 // foo.bar exactly foo.bar
207                 .replaceAll("//.", "////.")
208                         // foo.bar.1 or foo.bar.1.2 and so on...
209                 .replaceAll("////.////.////*", "////.//[A-Za-z_0-9.]")
210                         // packages only
211                 .replaceAll("//.//*", "//.[A-Za-z_0-9]*");
212 
213         final Pattern pattern = Pattern.compile(regex);
214         final Matcher matcher;
215 
216         final boolean matched;
217 
218         if (that instanceof String) {
219 
220             final String thatPackage = (String) that;
221             matcher = pattern.matcher(thatPackage);
222 
223             matched = matcher.matches();
224 
225         } else if (that instanceof JPackage) {
226 
227             final JPackage thatPackage = (JPackage) that;
228             matcher = pattern.matcher(thatPackage.getPath());
229 
230             matched = matcher.matches();
231 
232         } else {
233 
234             matched = false;
235         }
236 
237         if (matched)
238             log.debug(format("matched %s to %s", this.path, that));
239 
240         return matched;
241     }
242 
243 
244     /***
245      * <p>Matches by String equals against a String or JPackage</p>
246      *
247      * @param that <code>Object</code> of type <code>String</code> or
248      * <code>JPackage</code>
249      * @return <tt>true</tt> when the given <tt>Object</tt> is a supported type,
250      *         and an exact match to this <code>JPackage</code>.
251      */
252     private boolean prefectMatch(final Object that) {
253 
254         if (that instanceof String) {
255 
256             final String thatPackage = (String) that;
257             return this.path.equals(thatPackage);
258         }
259 
260         if (that instanceof JPackage) {
261 
262             final JPackage thatPackage = (JPackage) that;
263             return this.path.equals(thatPackage.getPath());
264         }
265 
266         return false;
267     }
268 }