001 
002 /*
003  *  JavaDoq 1.0 - DOCUment JAVA In Source
004  *  Copyright (C) 2008-2011  J.J.Liu<jianjunliu@126.com> <http://www.javadoq.com>
005  *  
006  *  This program is free software: you can redistribute it and/or modify
007  *  it under the terms of the GNU Affero General Public License as published by
008  *  the Free Software Foundation, either version 3 of the License, or
009  *  (at your option) any later version.
010  *  
011  *  This program is distributed in the hope that it will be useful,
012  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
013  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
014  *  GNU Affero General Public License for more details.
015  *  
016  *  You should have received a copy of the GNU Affero General Public License
017  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
018  */
019 
020 package com.javadoq;
021 
022 /**
023  * <p>Represents fully qualified names in Java source code.</p>
024  * <p>Note that a {@link JavaName} is read-only like a Java string.</p>
025  * 
026  * @author <a href="mailto:jianjunliu@126.com">J.J.Liu (Jianjun Liu)</a> at <a href="http://www.javadoq.com" target="_blank">http://www.javadoq.com</a>
027  */
028 public final class JavaName
029 {
030     /**
031      * <p>An empty {@link JavaName}.</p>
032      * @since 1.0
033      */
034     public final static JavaName EMPTY = new JavaName("");
035 
036     /**
037      * <p>Text of the {@link JavaName}.</p>
038      * @since 1.0
039      */
040     public final String text;
041     /**
042      * <p>Index of the first dot in the text.</p>
043      * @since 1.0
044      */
045     public final int firstDot;
046     /**
047      * <p>Index of the last dot in the text.</p>
048      * @since 1.0
049      */
050     public final int lastDot;
051     /**
052      * <p>Length of the text.</p>
053      * @since 1.0
054      */
055     public final int length;
056     /**
057      * <p>Whether the {@link JavaName} is simple.</p>
058      * @since 1.0
059      */
060     public final boolean isSimple;
061 
062     /**
063      * <p>Constructs a {@link JavaName} from a text string.</p>
064      * @param text The text for the {@link JavaName} being constructed.
065      * @since 1.0
066      */
067     public JavaName(String text) {
068         this.text = text;
069         firstDot = text.indexOf('.');
070         lastDot = text.lastIndexOf('.');
071         isSimple = (firstDot == -1 || lastDot == -1);
072         length = text.length();
073     }
074 
075     // Stores path
076     private String path;
077 
078     /**
079      * <p>Converts the {@link JavaName} to a directory path.</p>
080      * @return A string of the directory path.
081      * @since 1.0
082      */
083     public final String path() {
084         if (path == null) {
085             path = length == 0 ? "." : text.replace('.', '/');
086         }
087         return path;
088     }
089 
090     /**
091      * <p>Converts the {@link JavaName} to string.</p>
092      * @return The string representation of the {@link JavaName}.
093      * @since 1.0
094      */
095     @Override
096     public String toString() {
097         return text;
098     }
099 
100     /**
101      * <p>Determines if the current {@link JavaName} equals another object.</p>
102      * @param o An object.
103      * @return <tt>true</tt> if the given object is an instance of {@link JavaName} and its
104      * {@link #text} is equal to that of the current one.
105      * @since 1.0
106      */
107     @Override
108     public boolean equals(Object o) {
109         return o instanceof JavaName && ((JavaName)o).text.equals(text);
110     }
111 
112 
113     /**
114      * <p>Returns a hash code of the current {@link JavaName}.</p>
115      * @return A hash code of the current {@link JavaName} that is same as that of its {@link #text}.
116      * @since 1.0
117      */
118     @Override
119     public int hashCode() {
120         return text.hashCode();
121     }
122 
123     /**
124      * <p>Gets the first part of the {@link JavaName}.</p>
125      * @return The first part of the {@link JavaName} or <tt>null</tt> if 
126      * the {@link JavaName} is simple.
127      * @since 1.0
128      */
129     public final String getFirst() {
130         return isSimple ? null : text.substring(0, firstDot);
131     }
132 
133     /**
134      * <p>Gets the last part of the {@link JavaName}.</p>
135      * @return The last part of the {@link JavaName} or <tt>null</tt> if 
136      * the {@link JavaName} is simple.
137      * @since 1.0
138      */
139     public final String getLast() {
140         return isSimple ? null : text.substring(lastDot + 1);
141     }
142 
143     /**
144      * <p>Chops the first part of the {@link JavaName}.</p>
145      * @return The result {@link JavaName} or the current one itself if 
146      * it is simple.
147      * @since 1.0
148      */
149     public final JavaName chopFirst() {
150         if (isSimple) {
151             return this;
152         }
153         return new JavaName(text.substring(firstDot + 1));
154     }
155 
156     /**
157      * <p>Chops the last part of the {@link JavaName}.</p>
158      * @return The result {@link JavaName} or the current one itself if 
159      * it is simple.
160      * @since 1.0
161      */
162     public final JavaName chopLast() {
163         if (isSimple) {
164             return this;
165         }
166         return new JavaName(text.substring(0, lastDot));
167     }
168 
169     /**
170      * <p>Adds a simple name to the beginning of the current {@link JavaName}.</p>
171      * @param name A simple name to add.
172      * @return The result {@link JavaName}.
173      * @since 1.0
174      */
175     public final JavaName addFirst(String name) {
176         return name.length() == 0 ? this : new JavaName(
177                 length == 0 ? name : name + '.' + text
178         );
179     }
180 
181     /**
182      * <p>Adds a simple name to the end of the current {@link JavaName}.</p>
183      * @param name A simple name to add.
184      * @return The result {@link JavaName}.
185      * @since 1.0
186      */
187     public final JavaName addLast(String name) {
188         return name.length() == 0 ? this : new JavaName(
189                 length == 0 ? name : text + '.' + name
190         );
191     }
192 
193     /**
194      * <p>Determines if the current {@link JavaName} starts with a simple name.</p>
195      * @param simple A simple name.
196      * @return <tt>true</tt> if if the current {@link JavaName} starts with the
197      * specified simple name; <tt>false</tt>, otherwise.
198      * @since 1.0
199      */
200     public final boolean startsWith(String simple) {
201         return isSimple ? text.equals(simple) : getFirst().equals(simple);
202     }
203 
204     /**
205      * <p>Determines if the current {@link JavaName} starts with another name.</p>
206      * @param name Another {@link JavaName}.
207      * @return <tt>true</tt> if if the current {@link JavaName} starts with the
208      * specified {@link JavaName}; <tt>false</tt>, otherwise.
209      * @since 1.0
210      */
211     public final boolean startsWith(JavaName name) {
212         JavaName t = this;
213         while (!name.isSimple) {
214             String s = name.getFirst();
215             if (!t.startsWith(s)) {
216                 return false;
217             }
218             t = t.chopFirst(); name = name.chopFirst();
219         }
220         return t.startsWith(name.text);
221     }
222 
223     /**
224      * <p>Determines if the current {@link JavaName} ends with a simple name.</p>
225      * @param simple A simple name.
226      * @return <tt>true</tt> if if the current {@link JavaName} ends with the
227      * specified simple name; <tt>false</tt>, otherwise.
228      * @since 1.0
229      */
230     public final boolean endsWith(String simple) {
231         return isSimple ? text.equals(simple) : getLast().equals(simple);
232     }
233 
234     /**
235      * <p>Determines if the current {@link JavaName} ends with another name.</p>
236      * @param name Another {@link JavaName}.
237      * @return <tt>true</tt> if if the current {@link JavaName} ends with the
238      * specified {@link JavaName}; <tt>false</tt>, otherwise.
239      * @since 1.0
240      */
241     public final boolean endsWith(JavaName name) {
242         JavaName t = this;
243         while (!name.isSimple) {
244             String s = name.getLast();
245             if (!t.endsWith(s)) {
246                 return false;
247             }
248             t = t.chopLast(); name = name.chopLast();
249         }
250         return t.endsWith(name.text);
251     }
252 
253     /**
254      * <p>Chops the specified head of the {@link JavaName}.</p>
255      * @param head A head for the current name.
256      * @return The result {@link JavaName} or the current one does not starts 
257      * with the specified head.
258      * @since 1.0
259      */
260     public final JavaName chopFirst(JavaName head) {
261         if (head.length == 0 || !startsWith(head)) {
262             return this;
263         }
264         return new JavaName(text.substring(head.length +1));
265     }
266 
267     /**
268      * <p>Merges the specified {@link JavaName} to the end of the current one.</p>
269      * @param name A {@link JavaName}.
270      * @return The result {@link JavaName}.
271      * @since 1.0
272      */
273     public final JavaName merge(JavaName name) {
274         JavaName head = new JavaName(name.text);
275         JavaName tail = JavaName.EMPTY;
276         while (!endsWith(head)) {
277             if (head.isSimple) {
278                 return addLast(name.text);
279             }
280             tail = tail.addFirst(head.getLast());
281             head = head.chopLast();
282         }
283         return addLast(tail.text);
284     }
285 }