View Javadoc

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 &#139; Square &#155;".
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 }