1 /*
2 JavaProgrammingLanguage.java
3 Creation date : 19/06/2010
4 Copyright © Benjamin Croizet (graffity2199@yahoo.fr)
5
6 This program is free software; you can redistribute it and/or
7 modify it under the terms of the GNU General Public License
8 or GNU Lesser General Public License as published by the
9 Free Software Foundation; either version 3 of the License,
10 or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
16
17 You should have received copies of the GNU General Public License
18 and GNU Lesser General Public License along with this program;
19 if not, write to the Free Software Foundation, Inc.,
20 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 http://www.fsf.org/licensing/licenses/gpl.html
22 http://www.gnu.org/licenses/lgpl.html
23 */
24
25 package net.sourceforge.plantumldependency.cli.main.option.programminglanguage.argument;
26
27 import static java.lang.Class.forName;
28 import static java.util.logging.Level.FINE;
29 import static java.util.logging.Logger.getLogger;
30 import static net.sourceforge.plantumldependency.cli.constants.PlantUMLDependencyConstants.JAVA_LANG_PACKAGE;
31 import static net.sourceforge.plantumldependency.cli.constants.PlantUMLDependencyConstants.NATIVE_DEPENDENCY;
32 import static net.sourceforge.plantumldependency.cli.constants.RegularExpressionConstants.ANNOTATIONS_REGEXP;
33 import static net.sourceforge.plantumldependency.cli.constants.RegularExpressionConstants.JAVA_TYPE_REGEXP;
34 import static net.sourceforge.plantumldependency.cli.constants.RegularExpressionConstants.PACKAGE_REGEXP;
35 import static net.sourceforge.plantumldependency.cli.constants.RegularExpressionConstants.STANDARD_IMPORT_REGEXP;
36 import static net.sourceforge.plantumldependency.cli.constants.RegularExpressionConstants.STATIC_IMPORT_REGEXP;
37 import static net.sourceforge.plantumldependency.cli.constants.log.ErrorConstants.DEPENDENCY_NAME_NULL_ERROR;
38 import static net.sourceforge.plantumldependency.cli.constants.log.ErrorConstants.JAVA_TYPE_CANT_BE_EXTRACTED_ERROR;
39 import static net.sourceforge.plantumldependency.cli.constants.log.FineConstants.CREATING_DEPENDENCY_FINE;
40 import static net.sourceforge.plantumldependency.cli.constants.log.FineConstants.DEPENDENCY_ALREADY_SEEN_FINE;
41 import static net.sourceforge.plantumldependency.cli.constants.log.FineConstants.DEPENDENCY_NOT_SEEN_DEFAULT_TYPE_FINE;
42 import static net.sourceforge.plantumldependency.cli.constants.log.FineConstants.DEPENDENCY_NOT_SEEN_FINE;
43 import static net.sourceforge.plantumldependency.cli.constants.log.FineConstants.NO_PACKAGE_FOUND_FINE;
44 import static net.sourceforge.plantumldependency.cli.constants.log.FineConstants.UPDATING_DEPENDENCY_FINE;
45 import static net.sourceforge.plantumldependency.cli.generic.type.ImportType.NATIVE;
46 import static net.sourceforge.plantumldependency.cli.generic.type.ImportType.STANDARD;
47 import static net.sourceforge.plantumldependency.cli.generic.type.ImportType.STATIC;
48 import static net.sourceforge.plantumldependency.cli.generic.type.impl.DependencyTypeImpl.generateDependencyFullName;
49 import static net.sourceforge.plantumldependency.cli.main.option.programminglanguage.argument.java.type.JavaParentType.EXTENSION;
50 import static net.sourceforge.plantumldependency.cli.main.option.programminglanguage.argument.java.type.JavaParentType.IMPLEMENTATION;
51 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.AT_CHAR;
52 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.BACK_SLASH_CHAR;
53 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.CARRIAGE_RETURN_CHAR;
54 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.DOT_CHAR;
55 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.INFERIOR_CHAR;
56 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.LINE_CHAR;
57 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.QUOTATION_CHAR;
58 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.QUOTE_CHAR;
59 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.SLASH_CHAR;
60 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.SPACE_CHAR;
61 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.STAR_CHAR;
62 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.SUPERIOR_CHAR;
63 import static net.sourceforge.plantumldependency.common.constants.CharacterConstants.TAB_CHAR;
64 import static net.sourceforge.plantumldependency.common.constants.CommonConstants.BLANK_STRING;
65 import static net.sourceforge.plantumldependency.common.constants.CommonConstants.MINUS_ONE_RETURN_CODE;
66 import static net.sourceforge.plantumldependency.common.utils.log.LogUtils.buildLogString;
67 import static net.sourceforge.plantumldependency.common.utils.string.StringUtils.isEmpty;
68
69 import java.util.Set;
70 import java.util.TreeSet;
71 import java.util.logging.Logger;
72 import java.util.regex.Matcher;
73 import java.util.regex.Pattern;
74
75 import net.sourceforge.plantumldependency.cli.exception.PlantUMLDependencyException;
76 import net.sourceforge.plantumldependency.cli.generic.GenericDependency;
77 import net.sourceforge.plantumldependency.cli.generic.impl.GenericDependencyImpl;
78 import net.sourceforge.plantumldependency.cli.generic.type.DependencyType;
79 import net.sourceforge.plantumldependency.cli.generic.type.ImportDependenciesCollection;
80 import net.sourceforge.plantumldependency.cli.generic.type.impl.ImportDependenciesCollectionImpl;
81 import net.sourceforge.plantumldependency.cli.main.option.display.type.argument.DisplayType;
82 import net.sourceforge.plantumldependency.cli.main.option.programminglanguage.argument.java.JavaRawDependency;
83 import net.sourceforge.plantumldependency.cli.main.option.programminglanguage.argument.java.type.JavaParentType;
84 import net.sourceforge.plantumldependency.cli.main.option.programminglanguage.argument.java.type.JavaType;
85 import net.sourceforge.plantumldependency.cli.main.option.programminglanguage.context.ProgrammingLanguageContext;
86 import net.sourceforge.plantumldependency.cli.main.option.programminglanguage.context.impl.JavaProgrammingLanguageContext;
87
88 /**
89 * The Java {@link ProgrammingLanguage} implementation.
90 *
91 * @author Benjamin Croizet (<a href="mailto:graffity2199@yahoo.fr>graffity2199@yahoo.fr</a>)
92 * @since 1.0.0
93 * @version 1.4.0
94 */
95 class JavaProgrammingLanguage extends ProgrammingLanguage {
96
97 /** The class logger. */
98 private static final transient Logger LOGGER = getLogger(JavaProgrammingLanguage.class.getName());
99
100 /** Serial version UID. */
101 private static final long serialVersionUID = 62105384573195242L;
102
103 /**
104 * Creates the {@link GenericDependency} instance from the raw dependency and the java source
105 * file content.
106 *
107 * @param javaRawDependency
108 * the {@link JavaRawDependency} containing raw data of the dependency contained in
109 * the source file, mustn't be <code>null</code>.
110 * @param sourceFileContent
111 * the java source file content to analyze as a {@link String}, mustn't be
112 * <code>null</code>.
113 * @param programmingLanguageContext
114 * the context instance containing all dependencies which have already been seen in
115 * previous treatment, and other information which can be shared when parsing several
116 * source files, such as the display types options, mustn't be <code>null</code>.
117 * @return the {@link GenericDependency} instance read from the source file and from the raw
118 * dependency.
119 * @throws PlantUMLDependencyException
120 * if any exception occurs while reading or parsing the source file content.
121 * @since 1.0.0
122 */
123 private static GenericDependency createDependencyFromRaw(final JavaRawDependency javaRawDependency,
124 final String sourceFileContent, final ProgrammingLanguageContext programmingLanguageContext)
125 throws PlantUMLDependencyException {
126
127 final ImportDependenciesCollection importDependenciesCollection = extractImportDependencies(sourceFileContent,
128 programmingLanguageContext);
129
130 final Set < GenericDependency > annotationsDependencies = extractAnnotationsDependencies(sourceFileContent,
131 javaRawDependency.getType(), importDependenciesCollection, programmingLanguageContext,
132 javaRawDependency.getPackageName());
133
134 final boolean hasNativeMethods = javaRawDependency.hasNativeMethods();
135 if (hasNativeMethods) {
136 importDependenciesCollection.addImportDependencies(NATIVE, NATIVE_DEPENDENCY);
137 programmingLanguageContext.addSeenDependencies(NATIVE_DEPENDENCY);
138 }
139
140 final Set < GenericDependency > parentImplementationsDependencies = extractParentDependencies(
141 javaRawDependency.getType(), IMPLEMENTATION, javaRawDependency.getParentImplementations(),
142 importDependenciesCollection, programmingLanguageContext, javaRawDependency.getPackageName());
143
144 final Set < GenericDependency > parentExtentionsDependencies = extractParentDependencies(
145 javaRawDependency.getType(), EXTENSION, javaRawDependency.getParentExtensions(),
146 importDependenciesCollection, programmingLanguageContext, javaRawDependency.getPackageName());
147
148 final DependencyType dependencyType = javaRawDependency.getType().createDependencyType(
149 javaRawDependency.getName(), javaRawDependency.getPackageName(), javaRawDependency.isAbstract(),
150 importDependenciesCollection, parentExtentionsDependencies, parentImplementationsDependencies,
151 annotationsDependencies);
152 return createOrUpdateAbstractDependency(javaRawDependency, dependencyType, programmingLanguageContext);
153 }
154
155 /**
156 * Creates or updates the dependency type or the passed raw dependency. If the dependency has
157 * already been seen (i.e. it appears in the <code>programmingLanguageContext</code>) it just
158 * updates its {@link DependencyType}, otherwise it creates the dependency.
159 *
160 * @param javaRawDependency
161 * the {@link JavaRawDependency} containing raw data of the dependency contained in
162 * the source file, mustn't be <code>null</code>.
163 * @param dependencyType
164 * the {@link DependencyType} instance to set to the dependency, mustn't be
165 * <code>null</code>.
166 * @param programmingLanguageContext
167 * the context instance containing all dependencies which have already been seen in
168 * previous treatment, and other information which can be shared when parsing several
169 * source files, mustn't be <code>null</code>.
170 * @return the {@link GenericDependency} created or updated.
171 * @since 1.0.0
172 */
173 private static GenericDependency createOrUpdateAbstractDependency(final JavaRawDependency javaRawDependency,
174 final DependencyType dependencyType, final ProgrammingLanguageContext programmingLanguageContext) {
175 GenericDependency dependency = programmingLanguageContext.getParsedOrSeenDependency(javaRawDependency
176 .getFullName());
177
178 if (dependency == null) {
179 LOGGER.log(
180 FINE,
181 buildLogString(CREATING_DEPENDENCY_FINE, new Object[] {javaRawDependency.getFullName(),
182 dependencyType}));
183 dependency = new GenericDependencyImpl(dependencyType);
184 } else {
185 LOGGER.log(
186 FINE,
187 buildLogString(UPDATING_DEPENDENCY_FINE, new Object[] {javaRawDependency.getFullName(),
188 dependencyType}));
189 programmingLanguageContext.removePotentialJavaLangSeenDependency(javaRawDependency.getFullName());
190 dependency.setDependencyType(dependencyType);
191 }
192
193 return dependency;
194 }
195
196 /**
197 * Following the group found (i.e. the string defining an abstract class), tells if it is an
198 * abstract class or not. Basically, the string is either "abstract" or "".
199 *
200 * @param group
201 * the string defining an abstract class, can be <code>null</code>.
202 * @return <code>true</code> if the {@link String} defines and abstract class,
203 * <code>false</code> otherwise.
204 * @since 1.0.0
205 */
206 private static boolean extractAbstract(final String group) {
207 return isEmpty(group) ? false : true;
208 }
209
210 /**
211 * Following a java source file content, reads, parses and extracts all annotations dependencies
212 * (classes and methods). This method also adds the annotations dependencies in the dependencies
213 * {@link net.sourceforge.plantumldependency.cli.main.option.programminglanguage.context.ProgrammingLanguageContext}
214 * if they have not been already seen.
215 *
216 * @param javaSourceFileContent
217 * the java source file content to analyze as a {@link String}, mustn't be
218 * <code>null</code>.
219 * @param type
220 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
221 * @param importDependencies
222 * the {@link ImportDependenciesCollection} containing all import dependencies which
223 * are needed by the current dependency type to work, mustn't be <code>null</code>.
224 * @param programmingLanguageContext
225 * the context instance containing all dependencies which have already been seen in
226 * previous treatment, and other information which can be shared when parsing several
227 * source files, mustn't be <code>null</code>.
228 * @param currentPackageName
229 * the current dependency package name, mustn't be <code>null</code>.
230 * @return the {@link Set} of all annotations dependencies found in the java source file
231 * content. Returns an empty {@link Set} if no annotations have been found.
232 * @throws PlantUMLDependencyException
233 * if any exception occurs while getting or creating the annotation
234 * {@link GenericDependency} instance.
235 * @since 1.2.0
236 */
237 private static Set < GenericDependency > extractAnnotationsDependencies(final String javaSourceFileContent,
238 final JavaType type, final ImportDependenciesCollection importDependencies,
239 final ProgrammingLanguageContext programmingLanguageContext, final String currentPackageName)
240 throws PlantUMLDependencyException {
241 final Set < GenericDependency > annotationDependenciesSet = new TreeSet < GenericDependency >();
242
243 final Matcher matcher = ANNOTATIONS_REGEXP.matcher(javaSourceFileContent);
244
245 while (matcher.find()) {
246 final String annotationNameOrFullName = matcher.group(1);
247
248 if ("interface".equals(annotationNameOrFullName)) {
249 // FIXME the ANNOTATIONS_REGEXP takes "@interface" into account and it shouldn't, we
250 // must ignore it
251 } else {
252 final GenericDependency dependency = getOrCreateAnnotationDependency(annotationNameOrFullName,
253 currentPackageName, type, importDependencies, programmingLanguageContext);
254 annotationDependenciesSet.add(dependency);
255 }
256 }
257
258 return annotationDependenciesSet;
259 }
260
261 /**
262 * Following a java source file content, reads, parses and extracts all import dependencies
263 * (static and normal imports). This method also adds the import dependencies in the
264 * dependencies
265 * {@link net.sourceforge.plantumldependency.cli.main.option.programminglanguage.context.ProgrammingLanguageContext}
266 * if they have not been already seen.
267 *
268 * @param javaSourceFileContent
269 * the java source file content to analyze as a {@link String}, mustn't be
270 * <code>null</code>.
271 * @param programmingLanguageContext
272 * the context instance containing all dependencies which have already been seen in
273 * previous treatment, and other information which can be shared when parsing several
274 * source files, mustn't be <code>null</code>.
275 * @return the {@link ImportDependenciesCollection} of import dependencies found in the java
276 * source file content.
277 * @since 1.0.0
278 */
279 private static ImportDependenciesCollection extractImportDependencies(final String javaSourceFileContent,
280 final ProgrammingLanguageContext programmingLanguageContext) {
281
282 final ImportDependenciesCollection importDependenciesCollection = new ImportDependenciesCollectionImpl();
283
284 final Set < GenericDependency > standardImportDependenciesSet = extractImportDependenciesSet(
285 javaSourceFileContent, STANDARD_IMPORT_REGEXP, programmingLanguageContext);
286 importDependenciesCollection.addImportDependenciesSet(STANDARD, standardImportDependenciesSet);
287
288 final Set < GenericDependency > staticImportDependenciesSet = extractImportDependenciesSet(
289 javaSourceFileContent, STATIC_IMPORT_REGEXP, programmingLanguageContext);
290 importDependenciesCollection.addImportDependenciesSet(STATIC, staticImportDependenciesSet);
291
292 return importDependenciesCollection;
293 }
294
295 /**
296 * Following a java source file content and the import regular expression, reads, parses and
297 * extracts all import dependencies. This method also adds the import dependencies in the
298 * dependencies
299 * {@link net.sourceforge.plantumldependency.cli.main.option.programminglanguage.context.ProgrammingLanguageContext}
300 * if they have not been already seen.
301 *
302 * @param javaSourceFileContent
303 * the java source file content to analyze as a {@link String}, mustn't be
304 * <code>null</code>.
305 * @param importRegExp
306 * the import regular expression to analyze, mustn't be <code>null</code>.
307 * @param programmingLanguageContext
308 * the context instance containing all dependencies which have already been seen in
309 * previous treatment, and other information which can be shared when parsing several
310 * source files, mustn't be <code>null</code>.
311 * @return the {@link Set} of all import dependencies found in the java source file content.
312 * Returns an empty {@link Set} if no import has been found.
313 * @since 1.0.0
314 */
315 private static Set < GenericDependency > extractImportDependenciesSet(final String javaSourceFileContent,
316 final Pattern importRegExp, final ProgrammingLanguageContext programmingLanguageContext) {
317 final Set < GenericDependency > importDependenciesSet = new TreeSet < GenericDependency >();
318 final Matcher matcher = importRegExp.matcher(javaSourceFileContent);
319
320 while (matcher.find()) {
321 final String packageName = matcher.group(1).replace(SPACE_CHAR, BLANK_STRING);
322 final String name = matcher.group(2).trim();
323 final String fullName = generateDependencyFullName(packageName, name);
324 GenericDependency dependency = programmingLanguageContext.getParsedOrSeenDependency(fullName);
325 if (dependency == null) {
326 LOGGER.log(FINE, buildLogString(DEPENDENCY_NOT_SEEN_DEFAULT_TYPE_FINE, fullName));
327 dependency = new GenericDependencyImpl(name, packageName);
328 programmingLanguageContext.addSeenDependencies(dependency);
329 } else {
330 LOGGER.log(FINE, buildLogString(DEPENDENCY_ALREADY_SEEN_FINE, fullName));
331 }
332 importDependenciesSet.add(dependency);
333 }
334
335 return importDependenciesSet;
336 }
337
338 /**
339 * Following the group found (i.e. the string defining the dependency name), extract the
340 * interesting name of the dependency (i.e. without generic definition if any). Basically, the
341 * string can be "String", "Serializable" or "Rectangle ‹ Square ›".
342 *
343 * @param group
344 * the string defining the dependency name, mustn't be <code>null</code>.
345 * @return the dependency raw name, without any generic definition.
346 * @throws PlantUMLDependencyException
347 * if any exception occurs while extracting name.
348 * @since 1.0.0
349 */
350 private static String extractName(final String group) throws PlantUMLDependencyException {
351 if (isEmpty(group)) {
352 throw new PlantUMLDependencyException(DEPENDENCY_NAME_NULL_ERROR);
353 }
354
355 return group;
356 }
357
358 /**
359 * Reads, parses and extracts the package name in the passed java source file content.
360 *
361 * @param javaSourceFileContent
362 * the java source file content to analyze as a {@link String}, mustn't be
363 * <code>null</code>.
364 * @return the java package name if found, otherwise it returns a blank string.
365 * @since 1.0.0
366 */
367 private static String extractPackageName(final String javaSourceFileContent) {
368 String packageName = BLANK_STRING;
369 final Matcher matcher = PACKAGE_REGEXP.matcher(javaSourceFileContent);
370
371 if (matcher.find()) {
372 packageName = matcher.group(1).replace(SPACE_CHAR, BLANK_STRING);
373 } else {
374 LOGGER.log(FINE, NO_PACKAGE_FOUND_FINE);
375 }
376
377 return packageName;
378 }
379
380 /**
381 * Creates parent dependencies instances.
382 *
383 * @param type
384 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
385 * @param parentType
386 * the parent {@link JavaParentType}, mustn't be <code>null</code>.
387 * @param parents
388 * the {@link Set} of all parents' names or full names previously found, mustn't be
389 * <code>null</code>.
390 * @param importDependencies
391 * the {@link ImportDependenciesCollection} containing all import dependencies which
392 * are needed by the current dependency type to work, mustn't be <code>null</code>.
393 * @param programmingLanguageContext
394 * the context instance containing all dependencies which have already been seen in
395 * previous treatment, and other information which can be shared when parsing several
396 * source files, mustn't be <code>null</code>.
397 * @param currentPackageName
398 * the current dependency package name, mustn't be <code>null</code>.
399 * @return the {@link Set} containing all parents' as {@link GenericDependency} instances.
400 * @throws PlantUMLDependencyException
401 * if any exception occurs while creating parent {@link GenericDependency}
402 * instances.
403 * @since 1.0.0
404 */
405 private static Set < GenericDependency > extractParentDependencies(final JavaType type,
406 final JavaParentType parentType, final Set < String > parents,
407 final ImportDependenciesCollection importDependencies,
408 final ProgrammingLanguageContext programmingLanguageContext, final String currentPackageName)
409 throws PlantUMLDependencyException {
410
411 final Set < GenericDependency > parentsSet = new TreeSet < GenericDependency >();
412 for (final String parentNameOrFullName : parents) {
413 final GenericDependency dependency = getOrCreateParentDependency(type, parentType, parentNameOrFullName,
414 currentPackageName, importDependencies, programmingLanguageContext);
415 parentsSet.add(dependency);
416 }
417
418 return parentsSet;
419 }
420
421 /**
422 * Get the index of the character representing the end of a char content (i.e. the first quote
423 * character) in the passed string, starting from the passed index.
424 *
425 * @param beginningIndex
426 * the index where to start to look for the end of a character end content, must be
427 * between 0 and <code>str.length()</code>.
428 * @param str
429 * the string where to look for the end character.
430 * @return the index of the character representing the end of a string content. If not found,
431 * <code>beginningIndex</code> is returned.
432 * @since 1.0.0
433 */
434 private static int getNextEndOfCharContent(final int beginningIndex, final String str) {
435 int index = beginningIndex;
436 boolean found = false;
437
438 while (index < str.length() && !found) {
439 final char currentCharacter = str.charAt(index);
440 if (currentCharacter == BACK_SLASH_CHAR.charAt(0)) {
441 final char nextCharacter = str.charAt(index + 1);
442 if (nextCharacter == BACK_SLASH_CHAR.charAt(0) || nextCharacter == QUOTE_CHAR.charAt(0)) {
443 index++;
444 }
445 } else if (currentCharacter == QUOTE_CHAR.charAt(0)) {
446 found = true;
447 }
448 index++;
449 }
450
451 if (!found) {
452 index = beginningIndex;
453 }
454
455 return index;
456 }
457
458 /**
459 * Get the index of the character representing the end of a generic definition (i.e. the last
460 * superior character) in the passed string, starting from the passed index. If a comment char,
461 * a quote char or a quotation char is found, the search is stopped and the
462 * <code>beginningIndex</code> is returned.
463 *
464 * @param beginningIndex
465 * the index where to start to look for the end of a generic definition, must be
466 * between 0 and <code>str.length()</code>.
467 * @param str
468 * the string where to look for the end generic definition character.
469 * @return the index of the character representing the end of a generic definition. If not
470 * found, <code>beginningIndex</code> is returned.
471 * @since 1.0.0
472 */
473 private static int getNextEndOfGenericIndex(final int beginningIndex, final String str) {
474 int index = beginningIndex;
475 int numberOfGenerics = 1;
476 boolean stopSearch = false;
477
478 while (index < str.length() && numberOfGenerics != 0 && !stopSearch) {
479 final char currentCharacter = str.charAt(index);
480 if (currentCharacter == SLASH_CHAR.charAt(0)) {
481 if (index + 1 < str.length()) {
482 final char nextCharacter = str.charAt(index + 1);
483 if (nextCharacter == SLASH_CHAR.charAt(0)) {
484 stopSearch = true;
485 } else if (nextCharacter == STAR_CHAR.charAt(0)) {
486 stopSearch = true;
487 } else {
488 index++;
489 }
490 } else {
491 index++;
492 }
493 } else if (currentCharacter == QUOTE_CHAR.charAt(0)) {
494 stopSearch = true;
495 } else if (currentCharacter == QUOTATION_CHAR.charAt(0)) {
496 stopSearch = true;
497 } else if (currentCharacter == AT_CHAR.charAt(0)) {
498 stopSearch = true;
499 } else if (currentCharacter == INFERIOR_CHAR.charAt(0)) {
500 numberOfGenerics++;
501 index++;
502 } else if (currentCharacter == SUPERIOR_CHAR.charAt(0)) {
503 numberOfGenerics--;
504 index++;
505 } else {
506 index++;
507 }
508 }
509
510 if (numberOfGenerics != 0 || stopSearch) {
511 index = beginningIndex;
512 }
513
514 return index;
515 }
516
517 /**
518 * Get the index of the character representing the end of a multi line java comment (i.e. star +
519 * slash) in the passed string, starting from the passed index.
520 *
521 * @param beginningIndex
522 * the index where to start to look for the end comment character, must be between 0
523 * and <code>str.length()</code>.
524 * @param str
525 * the string where to look for the end comment character.
526 * @return the index of the character representing the end of a multi line java comment. If not
527 * found, <code>beginningIndex</code> is returned.
528 * @since 1.0.0
529 */
530 private static int getNextEndOfMultiLineCommentIndex(final int beginningIndex, final String str) {
531 int index = beginningIndex;
532 boolean found = false;
533
534 while (index < str.length() && !found) {
535 final char currentCharacter = str.charAt(index);
536 if (currentCharacter == STAR_CHAR.charAt(0)) {
537 if (index + 1 < str.length()) {
538 final char nextCharacter = str.charAt(index + 1);
539 if (nextCharacter == SLASH_CHAR.charAt(0)) {
540 index += 2;
541 found = true;
542 } else {
543 index++;
544 }
545 } else {
546 index++;
547 }
548 } else {
549 index++;
550 }
551 }
552
553 if (!found) {
554 index = beginningIndex;
555 }
556
557 return index;
558 }
559
560 /**
561 * Get the index of the character representing the end of a single line java comment (i.e. line
562 * separator) in the passed string, starting from the passed index.
563 *
564 * @param beginningIndex
565 * the index where to start to look for the end comment character, must be between 0
566 * and <code>str.length()</code>.
567 * @param str
568 * the string where to look for the end comment character.
569 * @return the index of the character representing the end of a single line java comment. If not
570 * found (i.e. end of file) , <code>str.length() - 1</code> is returned.
571 * @since 1.0.0
572 */
573 private static int getNextEndOfSimpleLineCommentIndex(final int beginningIndex, final String str) {
574 int index = beginningIndex;
575 boolean found = false;
576
577 while (index < str.length() && !found) {
578 final char currentCharacter = str.charAt(index);
579 if (currentCharacter == LINE_CHAR.charAt(0) || currentCharacter == CARRIAGE_RETURN_CHAR.charAt(0)) {
580 index++;
581 found = true;
582 } else {
583 index++;
584 }
585 }
586
587 return index;
588 }
589
590 /**
591 * Get the index of the character representing the end of a string content (i.e. the first
592 * quotation character) in the passed string, starting from the passed index.
593 *
594 * @param beginningIndex
595 * the index where to start to look for the end of a string end content, must be
596 * between 0 and <code>str.length()</code>.
597 * @param str
598 * the string where to look for the end string character.
599 * @return the index of the character representing the end of a string content. If not found,
600 * <code>beginningIndex</code> is returned.
601 * @since 1.0.0
602 */
603 private static int getNextEndOfStringContent(final int beginningIndex, final String str) {
604 int index = beginningIndex;
605 boolean found = false;
606
607 while (index < str.length() && !found) {
608 final char currentCharacter = str.charAt(index);
609 if (currentCharacter == BACK_SLASH_CHAR.charAt(0)) {
610 final char nextCharacter = str.charAt(index + 1);
611 if (nextCharacter == BACK_SLASH_CHAR.charAt(0) || nextCharacter == QUOTATION_CHAR.charAt(0)) {
612 index++;
613 }
614 } else if (currentCharacter == QUOTATION_CHAR.charAt(0)) {
615 found = true;
616 }
617 index++;
618 }
619
620 if (!found) {
621 index = beginningIndex;
622 }
623
624 return index;
625 }
626
627 /**
628 * Gets or creates the annotation dependency.
629 *
630 * @param annotationNameOrFullName
631 * the annotation dependency name or full name, mustn't be <code>null</code>.
632 * @param currentPackageName
633 * the current dependency package name, mustn't be <code>null</code>.
634 * @param type
635 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
636 * @param importDependencies
637 * the {@link ImportDependenciesCollection} containing all import dependencies which
638 * are needed by the current dependency type to work, mustn't be <code>null</code>.
639 * @param programmingLanguageContext
640 * the context instance containing all dependencies which have already been seen in
641 * previous treatment, and other information which can be shared when parsing several
642 * source files, mustn't be <code>null</code>.
643 * @return the annotation {@link GenericDependency} instance.
644 * @throws PlantUMLDependencyException
645 * if any exception occurs while getting or creating the annotation
646 * {@link GenericDependency} instance.
647 * @since 1.2.0
648 */
649 private static GenericDependency getOrCreateAnnotationDependency(final String annotationNameOrFullName,
650 final String currentPackageName, final JavaType type,
651 final ImportDependenciesCollection importDependencies,
652 final ProgrammingLanguageContext programmingLanguageContext) throws PlantUMLDependencyException {
653
654 GenericDependency dependency = null;
655 final int packageSeparatorIndex = annotationNameOrFullName.lastIndexOf(DOT_CHAR);
656
657 if (packageSeparatorIndex == MINUS_ONE_RETURN_CODE) {
658 dependency = getOrCreateAnnotationDependencyWithName(annotationNameOrFullName, currentPackageName, type,
659 importDependencies, programmingLanguageContext);
660 } else {
661 dependency = getOrCreateAnnotationDependencyWithFullName(annotationNameOrFullName, type,
662 importDependencies, programmingLanguageContext, packageSeparatorIndex);
663 }
664
665 return dependency;
666 }
667
668 /**
669 * Gets or creates the annotation dependency if it is described with its full name.
670 *
671 * @param annotationFullName
672 * the annotation dependency full name, mustn't be <code>null</code>.
673 * @param importDependencies
674 * the {@link ImportDependenciesCollection} containing all import dependencies which
675 * are needed by the current dependency type to work, mustn't be <code>null</code>.
676 * @param type
677 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
678 * @param programmingLanguageContext
679 * the context instance containing all dependencies which have already been seen in
680 * previous treatment, and other information which can be shared when parsing several
681 * source files, mustn't be <code>null</code>.
682 * @param packageSeparatorIndex
683 * the character separator index in the <code>annotationFullName</code> string which
684 * separates the package from the dependency name, must be >= 0 and inferior to
685 * <code>annotationFullName.length()</code>.
686 * @return the annotation {@link GenericDependency} instance.
687 * @throws PlantUMLDependencyException
688 * if any exception occurs while getting or creating the parent
689 * {@link GenericDependency} instance.
690 * @since 1.2.0
691 */
692 private static GenericDependency getOrCreateAnnotationDependencyWithFullName(final String annotationFullName,
693 final JavaType type, final ImportDependenciesCollection importDependencies,
694 final ProgrammingLanguageContext programmingLanguageContext, final int packageSeparatorIndex)
695 throws PlantUMLDependencyException {
696 final String parentName = annotationFullName.substring(packageSeparatorIndex + 1);
697 final String parentPackageName = annotationFullName.substring(0, packageSeparatorIndex);
698 GenericDependency dependency = importDependencies.findDependency(parentName, parentPackageName);
699
700 if (dependency == null) {
701 dependency = programmingLanguageContext.getParsedOrSeenDependency(annotationFullName);
702 if (dependency == null) {
703 final DependencyType dependencyType = type
704 .createAnnotationDependencyType(parentName, parentPackageName);
705 LOGGER.log(FINE,
706 buildLogString(DEPENDENCY_NOT_SEEN_FINE, new Object[] {annotationFullName, dependencyType}));
707 dependency = new GenericDependencyImpl(dependencyType);
708 programmingLanguageContext.addSeenDependencies(dependency);
709 } else {
710 LOGGER.log(FINE, buildLogString(DEPENDENCY_ALREADY_SEEN_FINE, annotationFullName));
711 }
712 } else {
713 dependency = getOrCreateAnnotationDependencyWithImportDependency(type, programmingLanguageContext,
714 dependency);
715 }
716
717 return dependency;
718 }
719
720 /**
721 * Gets or creates the annotation dependency if it has been found in the imports.
722 *
723 * @param type
724 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
725 * @param programmingLanguageContext
726 * the context instance containing all dependencies which have already been seen in
727 * previous treatment, and other information which can be shared when parsing several
728 * source files, mustn't be <code>null</code>.
729 * @param importDependency
730 * the dependency which has been found from the imports, mustn't be <code>null</code>
731 * .
732 * @return the annotation {@link GenericDependency} instance.
733 * @throws PlantUMLDependencyException
734 * if any exception occurs while getting or creating the annotation
735 * {@link GenericDependency} instance.
736 * @since 1.2.0
737 */
738 private static GenericDependency getOrCreateAnnotationDependencyWithImportDependency(final JavaType type,
739 final ProgrammingLanguageContext programmingLanguageContext, final GenericDependency importDependency)
740 throws PlantUMLDependencyException {
741 GenericDependency dependency = importDependency;
742 final GenericDependency parsedDependency = programmingLanguageContext.getParsedDependency(importDependency
743 .getFullName());
744 if (parsedDependency == null) {
745 final DependencyType dependencyType = type.createAnnotationDependencyType(importDependency.getName(),
746 importDependency.getPackageName());
747 dependency.setDependencyType(dependencyType);
748 } else {
749 LOGGER.log(FINE, buildLogString(DEPENDENCY_ALREADY_SEEN_FINE, dependency.getFullName()));
750 dependency = parsedDependency;
751 }
752
753 return dependency;
754 }
755
756 /**
757 * Gets or creates the annotation dependency if it is not described with its full name, i.e. if
758 * it is not in the import, or in the same package or in the "java.lang" package.
759 *
760 * @param annotationName
761 * the annotation dependency name, mustn't be <code>null</code>.
762 * @param currentPackageName
763 * the current dependency package name, mustn't be <code>null</code>.
764 * @param type
765 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
766 * @param importDependencies
767 * the {@link ImportDependenciesCollection} containing all import dependencies which
768 * are needed by the current dependency type to work, mustn't be <code>null</code>.
769 * @param programmingLanguageContext
770 * the context instance containing all dependencies which have already been seen in
771 * previous treatment, and other information which can be shared when parsing several
772 * source files, mustn't be <code>null</code>.
773 * @return the annotation {@link GenericDependency} instance.
774 * @throws PlantUMLDependencyException
775 * if any exception occurs while getting or creating the annotation
776 * {@link GenericDependency} instance.
777 * @since 1.2.0
778 */
779 private static GenericDependency getOrCreateAnnotationDependencyWithName(final String annotationName,
780 final String currentPackageName, final JavaType type,
781 final ImportDependenciesCollection importDependencies,
782 final ProgrammingLanguageContext programmingLanguageContext) throws PlantUMLDependencyException {
783 GenericDependency dependency = importDependencies.findDependency(annotationName);
784
785 if (dependency == null) {
786 final String annotationFullNameWithSamePackage = generateDependencyFullName(currentPackageName,
787 annotationName);
788 dependency = programmingLanguageContext.getParsedOrSeenDependency(annotationFullNameWithSamePackage);
789 if (dependency == null) {
790 dependency = getOrCreateAnnotationDependencyWithNameFromJavaLangOrSamePackage(currentPackageName, type,
791 programmingLanguageContext, annotationName, annotationFullNameWithSamePackage);
792 } else {
793 LOGGER.log(FINE, buildLogString(DEPENDENCY_ALREADY_SEEN_FINE, annotationFullNameWithSamePackage));
794 }
795 } else {
796 dependency = getOrCreateAnnotationDependencyWithImportDependency(type, programmingLanguageContext,
797 dependency);
798 }
799
800 return dependency;
801 }
802
803 /**
804 * Gets or creates the annotation dependency if it is not described with its full name, i.e. if
805 * it is in the same package or in the "java.lang" package.
806 *
807 * @param currentPackageName
808 * the current dependency package name, mustn't be <code>null</code>.
809 * @param type
810 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
811 * @param programmingLanguageContext
812 * the context instance containing all dependencies which have already been seen in
813 * previous treatment, and other information which can be shared when parsing several
814 * source files, mustn't be <code>null</code>.
815 * @param annotationName
816 * the annotation dependency name, mustn't be <code>null</code>.
817 * @param annotationFullNameWithSamePackage
818 * the annotation dependency full name with the same package as the current
819 * dependency, mustn't be <code>null</code>.
820 * @return the annotation {@link GenericDependency} instance.
821 * @throws PlantUMLDependencyException
822 * if any exception occurs while getting or creating the parent
823 * {@link GenericDependency} instance.
824 * @since 1.2.0
825 */
826 private static GenericDependency getOrCreateAnnotationDependencyWithNameFromJavaLangOrSamePackage(
827 final String currentPackageName, final JavaType type,
828 final ProgrammingLanguageContext programmingLanguageContext, final String annotationName,
829 final String annotationFullNameWithSamePackage) throws PlantUMLDependencyException {
830 final DependencyType dependencyType = type.createAnnotationDependencyType(annotationName, currentPackageName);
831 LOGGER.log(
832 FINE,
833 buildLogString(DEPENDENCY_NOT_SEEN_FINE, new Object[] {annotationFullNameWithSamePackage,
834 dependencyType}));
835 final GenericDependency dependency = new GenericDependencyImpl(dependencyType);
836
837 try {
838 final String potentialJavaLangAnnotationFullName = generateDependencyFullName(JAVA_LANG_PACKAGE,
839 annotationName);
840 forName(potentialJavaLangAnnotationFullName);
841 programmingLanguageContext.addPotentialJavaLangSeenDependencies(dependency);
842 } catch (final ClassNotFoundException e) {
843 programmingLanguageContext.addSeenDependencies(dependency);
844 }
845
846 return dependency;
847 }
848
849 /**
850 * Gets or creates the parent dependency.
851 *
852 * @param type
853 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
854 * @param parentType
855 * the parent {@link JavaParentType}, mustn't be <code>null</code>.
856 * @param parentNameOrFullName
857 * the parent dependency name or full name, mustn't be <code>null</code>.
858 * @param currentPackageName
859 * the current dependency package name, mustn't be <code>null</code>.
860 * @param importDependencies
861 * the {@link ImportDependenciesCollection} containing all import dependencies which
862 * are needed by the current dependency type to work, mustn't be <code>null</code>.
863 * @param programmingLanguageContext
864 * the context instance containing all dependencies which have already been seen in
865 * previous treatment, and other information which can be shared when parsing several
866 * source files, mustn't be <code>null</code>.
867 * @return the parent {@link GenericDependency} instance.
868 * @throws PlantUMLDependencyException
869 * if any exception occurs while getting or creating the parent
870 * {@link GenericDependency} instance.
871 * @since 1.0.0
872 */
873 private static GenericDependency getOrCreateParentDependency(final JavaType type, final JavaParentType parentType,
874 final String parentNameOrFullName, final String currentPackageName,
875 final ImportDependenciesCollection importDependencies,
876 final ProgrammingLanguageContext programmingLanguageContext) throws PlantUMLDependencyException {
877
878 GenericDependency dependency = null;
879 final int packageSeparatorIndex = parentNameOrFullName.lastIndexOf(DOT_CHAR);
880
881 if (packageSeparatorIndex == MINUS_ONE_RETURN_CODE) {
882 dependency = getOrCreateParentDependencyWithName(type, parentType, currentPackageName, importDependencies,
883 programmingLanguageContext, parentNameOrFullName);
884 } else {
885 dependency = getOrCreateParentDependencyWithFullName(type, parentType, parentNameOrFullName,
886 importDependencies, programmingLanguageContext, packageSeparatorIndex);
887 }
888
889 return dependency;
890 }
891
892 /**
893 * Gets or creates the parent dependency if it is described with its full name.
894 *
895 * @param type
896 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
897 * @param parentType
898 * the parent {@link JavaParentType}, mustn't be <code>null</code>.
899 * @param parentFullName
900 * the parent dependency full name, mustn't be <code>null</code>.
901 * @param importDependencies
902 * the {@link ImportDependenciesCollection} containing all import dependencies which
903 * are needed by the current dependency type to work, mustn't be <code>null</code>.
904 * @param programmingLanguageContext
905 * the context instance containing all dependencies which have already been seen in
906 * previous treatment, and other information which can be shared when parsing several
907 * source files, mustn't be <code>null</code>.
908 * @param packageSeparatorIndex
909 * the character separator index in the <code>parentFullName</code> string which
910 * separates the package from the dependency name, must be >= 0 and inferior to
911 * <code>parentFullName.length()</code>.
912 * @return the parent {@link GenericDependency} instance.
913 * @throws PlantUMLDependencyException
914 * if any exception occurs while getting or creating the parent
915 * {@link GenericDependency} instance.
916 * @since 1.0.0
917 */
918 private static GenericDependency getOrCreateParentDependencyWithFullName(final JavaType type,
919 final JavaParentType parentType, final String parentFullName,
920 final ImportDependenciesCollection importDependencies,
921 final ProgrammingLanguageContext programmingLanguageContext, final int packageSeparatorIndex)
922 throws PlantUMLDependencyException {
923 final String parentName = parentFullName.substring(packageSeparatorIndex + 1);
924 final String parentPackageName = parentFullName.substring(0, packageSeparatorIndex);
925 GenericDependency dependency = importDependencies.findDependency(parentName, parentPackageName);
926
927 if (dependency == null) {
928 dependency = programmingLanguageContext.getParsedOrSeenDependency(parentFullName);
929 if (dependency == null) {
930 final DependencyType dependencyType = type.createParentDependencyType(parentType, parentName,
931 parentPackageName);
932 LOGGER.log(FINE,
933 buildLogString(DEPENDENCY_NOT_SEEN_FINE, new Object[] {parentFullName, dependencyType}));
934 dependency = new GenericDependencyImpl(dependencyType);
935 programmingLanguageContext.addSeenDependencies(dependency);
936 } else {
937 LOGGER.log(FINE, buildLogString(DEPENDENCY_ALREADY_SEEN_FINE, parentFullName));
938 }
939 } else {
940 dependency = getOrCreateParentDependencyWithImportDependency(type, parentType, programmingLanguageContext,
941 dependency);
942 }
943
944 return dependency;
945 }
946
947 /**
948 * Gets or creates the parent dependency if it has been found in the imports.
949 *
950 * @param type
951 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
952 * @param parentType
953 * the parent {@link JavaParentType}, mustn't be <code>null</code>.
954 * @param programmingLanguageContext
955 * the context instance containing all dependencies which have already been seen in
956 * previous treatment, and other information which can be shared when parsing several
957 * source files, mustn't be <code>null</code>.
958 * @param importDependency
959 * the dependency which has been found from the imports, mustn't be <code>null</code>
960 * .
961 * @return the parent {@link GenericDependency} instance.
962 * @throws PlantUMLDependencyException
963 * if any exception occurs while getting or creating the annotation
964 * {@link GenericDependency} instance.
965 * @since 1.2.0
966 */
967 private static GenericDependency getOrCreateParentDependencyWithImportDependency(final JavaType type,
968 final JavaParentType parentType, final ProgrammingLanguageContext programmingLanguageContext,
969 final GenericDependency importDependency) throws PlantUMLDependencyException {
970 GenericDependency dependency = importDependency;
971 final GenericDependency parsedDependency = programmingLanguageContext.getParsedDependency(importDependency
972 .getFullName());
973 if (parsedDependency == null) {
974 final DependencyType dependencyType = type.createParentDependencyType(parentType, dependency.getName(),
975 dependency.getPackageName());
976 dependency.setDependencyType(dependencyType);
977 } else {
978 LOGGER.log(FINE, buildLogString(DEPENDENCY_ALREADY_SEEN_FINE, dependency.getFullName()));
979 dependency = parsedDependency;
980 }
981
982 return dependency;
983 }
984
985 /**
986 * Gets or creates the parent dependency if it is not described with its full name, i.e. if it
987 * is not in the import, or in the same package or in the "java.lang" package.
988 *
989 * @param type
990 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
991 * @param parentType
992 * the parent {@link JavaParentType}, mustn't be <code>null</code>.
993 * @param currentPackageName
994 * the current dependency package name, mustn't be <code>null</code>.
995 * @param importDependencies
996 * the {@link ImportDependenciesCollection} containing all import dependencies which
997 * are needed by the current dependency type to work, mustn't be <code>null</code>.
998 * @param programmingLanguageContext
999 * the context instance containing all dependencies which have already been seen in
1000 * previous treatment, and other information which can be shared when parsing several
1001 * source files, mustn't be <code>null</code>.
1002 * @param parentName
1003 * the parent dependency name, mustn't be <code>null</code>.
1004 * @return the parent {@link GenericDependency} instance.
1005 * @throws PlantUMLDependencyException
1006 * if any exception occurs while getting or creating the parent
1007 * {@link GenericDependency} instance.
1008 * @since 1.0.0
1009 */
1010 private static GenericDependency getOrCreateParentDependencyWithName(final JavaType type,
1011 final JavaParentType parentType, final String currentPackageName,
1012 final ImportDependenciesCollection importDependencies,
1013 final ProgrammingLanguageContext programmingLanguageContext, final String parentName)
1014 throws PlantUMLDependencyException {
1015 GenericDependency dependency = importDependencies.findDependency(parentName);
1016
1017 if (dependency == null) {
1018 final String parentFullNameWithSamePackage = generateDependencyFullName(currentPackageName, parentName);
1019 dependency = programmingLanguageContext.getParsedOrSeenDependency(parentFullNameWithSamePackage);
1020 if (dependency == null) {
1021 dependency = getOrCreateParentDependencyWithNameFromJavaLangOrSamePackage(type, parentType,
1022 currentPackageName, programmingLanguageContext, parentName, parentFullNameWithSamePackage);
1023 } else {
1024 LOGGER.log(FINE, buildLogString(DEPENDENCY_ALREADY_SEEN_FINE, parentFullNameWithSamePackage));
1025 }
1026 } else {
1027 dependency = getOrCreateParentDependencyWithImportDependency(type, parentType, programmingLanguageContext,
1028 dependency);
1029 }
1030
1031 return dependency;
1032 }
1033
1034 /**
1035 * Gets or creates the parent dependency if it is not described with its full name, i.e. if it
1036 * is in the same package or in the "java.lang" package.
1037 *
1038 * @param type
1039 * the current dependency {@link JavaType}, mustn't be <code>null</code>.
1040 * @param parentType
1041 * the parent {@link JavaParentType}, mustn't be <code>null</code>.
1042 * @param currentPackageName
1043 * the current dependency package name, mustn't be <code>null</code>.
1044 * @param programmingLanguageContext
1045 * the context instance containing all dependencies which have already been seen in
1046 * previous treatment, and other information which can be shared when parsing several
1047 * source files, mustn't be <code>null</code>.
1048 * @param parentName
1049 * the parent dependency name, mustn't be <code>null</code>.
1050 * @param parentFullNameWithSamePackage
1051 * the parent dependency full name with the same package as the current dependency,
1052 * mustn't be <code>null</code>.
1053 * @return the parent {@link GenericDependency} instance.
1054 * @throws PlantUMLDependencyException
1055 * if any exception occurs while getting or creating the parent
1056 * {@link GenericDependency} instance.
1057 * @since 1.0.0
1058 */
1059 private static GenericDependency getOrCreateParentDependencyWithNameFromJavaLangOrSamePackage(final JavaType type,
1060 final JavaParentType parentType, final String currentPackageName,
1061 final ProgrammingLanguageContext programmingLanguageContext, final String parentName,
1062 final String parentFullNameWithSamePackage) throws PlantUMLDependencyException {
1063 final DependencyType dependencyType = type.createParentDependencyType(parentType, parentName,
1064 currentPackageName);
1065 LOGGER.log(FINE,
1066 buildLogString(DEPENDENCY_NOT_SEEN_FINE, new Object[] {parentFullNameWithSamePackage, dependencyType}));
1067 final GenericDependency dependency = new GenericDependencyImpl(dependencyType);
1068
1069 try {
1070 final String potentialJavaLangParentFullName = generateDependencyFullName(JAVA_LANG_PACKAGE, parentName);
1071 forName(potentialJavaLangParentFullName);
1072 programmingLanguageContext.addPotentialJavaLangSeenDependencies(dependency);
1073 } catch (final ClassNotFoundException e) {
1074 programmingLanguageContext.addSeenDependencies(dependency);
1075 }
1076
1077 return dependency;
1078 }
1079
1080 /**
1081 * Reads, parses and extracts the {@link GenericDependency} instance from the passed java source
1082 * file content.
1083 *
1084 * @param javaSourceFileContent
1085 * the java source file content to analyze as a {@link String}, mustn't be
1086 * <code>null</code>.
1087 * @param programmingLanguageContext
1088 * the context instance containing all dependencies which have already been seen in
1089 * previous treatment, and other information which can be shared when parsing several
1090 * source files, mustn't be <code>null</code>.
1091 * @return the {@link GenericDependency} instance extracted from the java source file content.
1092 * @throws PlantUMLDependencyException
1093 * if any exception occurs while reading or creating the {@link GenericDependency}
1094 * instance.
1095 * @since 1.0.0
1096 */
1097 private static GenericDependency readDependencyFromPreparedFile(final String javaSourceFileContent,
1098 final ProgrammingLanguageContext programmingLanguageContext) throws PlantUMLDependencyException {
1099 final JavaRawDependency javaRawDependency = readJavaRawDependencyFromPreparedFile(javaSourceFileContent);
1100 return createDependencyFromRaw(javaRawDependency, javaSourceFileContent, programmingLanguageContext);
1101 }
1102
1103 /**
1104 * Reads, parses and extracts the {@link JavaRawDependency} instance from the passed java source
1105 * file content.
1106 *
1107 * @param javaSourceFileContent
1108 * the java source file content to analyze as a {@link String}, mustn't be
1109 * <code>null</code>.
1110 * @return the {@link JavaRawDependency} instance extracted from the java source file content.
1111 * @throws PlantUMLDependencyException
1112 * if any exception occurs while reading or creating the {@link JavaRawDependency}
1113 * instance.
1114 * @since 1.0.0
1115 */
1116 private static JavaRawDependency readJavaRawDependencyFromPreparedFile(final String javaSourceFileContent)
1117 throws PlantUMLDependencyException {
1118 final JavaRawDependency javaRawDependency = new JavaRawDependency();
1119
1120 final Matcher matcher = JAVA_TYPE_REGEXP.matcher(javaSourceFileContent);
1121 if (matcher.find()) {
1122 final String packageName = extractPackageName(javaSourceFileContent);
1123 javaRawDependency.setPackageName(packageName);
1124
1125 final boolean isAbstract = extractAbstract(matcher.group(1));
1126 javaRawDependency.setAbstract(isAbstract);
1127
1128 final JavaType type = JavaType.valueOfIgnoringCase(matcher.group(2));
1129 javaRawDependency.setType(type);
1130
1131 final String name = extractName(matcher.group(3));
1132 javaRawDependency.setName(name);
1133
1134 final Set < String > parentImplementations = type.extractParentImplementations(matcher.group(5));
1135 javaRawDependency.setParentImplementations(parentImplementations);
1136
1137 final Set < String > parentExtensions = type.extractParentExtensions(matcher.group(4));
1138 javaRawDependency.setParentExtensions(parentExtensions);
1139
1140 final boolean nativeMethods = type.extractNativeMethods(javaSourceFileContent);
1141 javaRawDependency.setNativeMethods(nativeMethods);
1142 } else {
1143 throw new PlantUMLDependencyException(buildLogString(JAVA_TYPE_CANT_BE_EXTRACTED_ERROR,
1144 javaSourceFileContent));
1145 }
1146
1147 return javaRawDependency;
1148 }
1149
1150 /**
1151 * Prepares the java source file content to remove all unnecessary strings which are not used in
1152 * the analysis, i.e. comments, generic and string content within the source code.
1153 *
1154 * @param javaSourceFileContent
1155 * the java source file content to analyze as a {@link String}, mustn't be
1156 * <code>null</code>.
1157 * @return the new java source file content without all unnecessary strings which are not used
1158 * in the analysis.
1159 * @since 1.0.0
1160 */
1161 private static String removeSourceFileCommentsAndGenerics(final String javaSourceFileContent) {
1162 final StringBuilder buffer = new StringBuilder();
1163
1164 int cursor = 0;
1165 while (cursor < javaSourceFileContent.length()) {
1166 final char currentCharacter = javaSourceFileContent.charAt(cursor);
1167 if (currentCharacter == LINE_CHAR.charAt(0) || currentCharacter == CARRIAGE_RETURN_CHAR.charAt(0)
1168 || currentCharacter == TAB_CHAR.charAt(0)) {
1169 buffer.append(SPACE_CHAR);
1170 cursor++;
1171 } else if (currentCharacter == SLASH_CHAR.charAt(0)) {
1172 if (cursor + 1 < javaSourceFileContent.length()) {
1173 final char nextCharacter = javaSourceFileContent.charAt(cursor + 1);
1174 if (nextCharacter == SLASH_CHAR.charAt(0)) {
1175 cursor = getNextEndOfSimpleLineCommentIndex(cursor + 2, javaSourceFileContent);
1176 } else if (nextCharacter == STAR_CHAR.charAt(0)) {
1177 buffer.append(SPACE_CHAR);
1178 cursor = getNextEndOfMultiLineCommentIndex(cursor + 2, javaSourceFileContent);
1179 } else {
1180 buffer.append(currentCharacter);
1181 cursor++;
1182 }
1183 } else {
1184 buffer.append(currentCharacter);
1185 cursor++;
1186 }
1187 } else if (currentCharacter == INFERIOR_CHAR.charAt(0)) {
1188 if (cursor + 1 < javaSourceFileContent.length()) {
1189 cursor = getNextEndOfGenericIndex(cursor + 1, javaSourceFileContent);
1190 } else {
1191 buffer.append(currentCharacter);
1192 cursor++;
1193 }
1194 } else if (currentCharacter == QUOTE_CHAR.charAt(0)) {
1195 if (cursor + 1 < javaSourceFileContent.length()) {
1196 cursor = getNextEndOfCharContent(cursor + 1, javaSourceFileContent);
1197 buffer.append(QUOTE_CHAR);
1198 buffer.append(QUOTE_CHAR);
1199 } else {
1200 buffer.append(currentCharacter);
1201 cursor++;
1202 }
1203 } else if (currentCharacter == QUOTATION_CHAR.charAt(0)) {
1204 if (cursor + 1 < javaSourceFileContent.length()) {
1205 cursor = getNextEndOfStringContent(cursor + 1, javaSourceFileContent);
1206 buffer.append(QUOTATION_CHAR);
1207 buffer.append(QUOTATION_CHAR);
1208 } else {
1209 buffer.append(currentCharacter);
1210 cursor++;
1211 }
1212 } else {
1213 buffer.append(currentCharacter);
1214 cursor++;
1215 }
1216 }
1217
1218 return buffer.toString().trim();
1219 }
1220
1221 /**
1222 * Default constructor.
1223 *
1224 * @param programmingLanguageName
1225 * the programming language name to get the instance from, mustn't be
1226 * <code>null</code> nor empty.
1227 * @since 1.0.0
1228 */
1229 protected JavaProgrammingLanguage(final String programmingLanguageName) {
1230 super(programmingLanguageName);
1231 }
1232
1233 /**
1234 * {@inheritDoc}
1235 *
1236 * @since 1.0.0
1237 */
1238 @Override
1239 public ProgrammingLanguageContext createNewContext(final Set < DisplayType > displayTypesOpts,
1240 final Pattern displayPackageNamePattern, final Pattern displayNamePattern) {
1241 return new JavaProgrammingLanguageContext(displayTypesOpts, displayPackageNamePattern, displayNamePattern);
1242 }
1243
1244 /**
1245 * {@inheritDoc}
1246 *
1247 * @since 1.1.1
1248 */
1249 @Override
1250 public GenericDependency readDependencyFromFile(final String sourceFileContent,
1251 final ProgrammingLanguageContext programmingLanguageContext) throws PlantUMLDependencyException {
1252 final String preparedSourceFileContent = removeSourceFileCommentsAndGenerics(sourceFileContent);
1253 final GenericDependency genericDependency = readDependencyFromPreparedFile(preparedSourceFileContent,
1254 programmingLanguageContext);
1255 programmingLanguageContext.addParsedAndSeenDependencies(genericDependency);
1256 return genericDependency;
1257 }
1258 }