001////////////////////////////////////////////////////////////////////////////////
002// checkstyle: Checks Java source code for adherence to a set of rules.
003// Copyright (C) 2001-2014  Oliver Burn
004//
005// This library is free software; you can redistribute it and/or
006// modify it under the terms of the GNU Lesser General Public
007// License as published by the Free Software Foundation; either
008// version 2.1 of the License, or (at your option) any later version.
009//
010// This library is distributed in the hope that it will be useful,
011// but WITHOUT ANY WARRANTY; without even the implied warranty of
012// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
013// Lesser General Public License for more details.
014//
015// You should have received a copy of the GNU Lesser General Public
016// License along with this library; if not, write to the Free Software
017// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
018////////////////////////////////////////////////////////////////////////////////
019
020package com.puppycrawl.tools.checkstyle.checks.imports;
021
022import java.util.ArrayList;
023import java.util.List;
024import java.util.StringTokenizer;
025import java.util.regex.Pattern;
026
027import com.puppycrawl.tools.checkstyle.api.Check;
028import com.puppycrawl.tools.checkstyle.api.DetailAST;
029import com.puppycrawl.tools.checkstyle.api.FullIdent;
030import com.puppycrawl.tools.checkstyle.api.TokenTypes;
031import com.puppycrawl.tools.checkstyle.api.Utils;
032
033/**
034 * <p>
035 * Checks that the groups of import declarations appear in the order specified
036 * by the user. If there is an import but its group is not specified in the
037 * configuration such an import should be placed at the end of the import list.
038 * </p>
039 * The rule consists of:
040 *
041 * <pre>
042 * STATIC group. This group sets the ordering of static imports.
043 * </pre>
044 *
045 * <pre>
046 * SAME_PACKAGE(n) group. This group sets the ordering of the same package imports.
047 * 'n' - a number of the first package domains. For example:
048 * </pre>
049 *
050 * <pre>
051 * package java.util.concurrent;
052 *
053 * import java.util.regex.Pattern;
054 * import java.util.List;
055 * import java.util.StringTokenizer;
056 * import java.util.regex.Pattern;
057 * import java.util.*;
058 * import java.util.concurrent.AbstractExecutorService;
059 * import java.util.concurrent.*;
060 *
061 * And we have such configuration: SAME_PACKAGE (3).
062 * Same package imports are java.util.*, java.util.concurrent.*,
063 * java.util.concurrent.AbstractExecutorService,
064 * java.util.List and java.util.StringTokenizer
065 * </pre>
066 *
067 * <pre>
068 * THIRD_PARTY_PACKAGE group. This group sets ordering of third party imports.
069 * Third party imports are all imports except STATIC,
070 * SAME_PACKAGE(n) and STANDARD_JAVA_PACKAGE.
071 * </pre>
072 *
073 * <pre>
074 * STANDARD_JAVA_PACKAGE group. This group sets ordering of standard java (java|javax) imports.
075 * </pre>
076 *
077 * <pre>
078 * SPECIAL_IMPORTS group. This group may contains some imports
079 * that have particular meaning for the user.
080 * </pre>
081 *
082 * <p>
083 * NOTICE!
084 * </p>
085 * <p>
086 * Use the separator '###' between rules.
087 * </p>
088 * <p>
089 * To set RegExps for THIRD_PARTY_PACKAGE and STANDARD_JAVA_PACKAGE groups use
090 * thirdPartyPackageRegExp and standardPackageRegExp options.
091 * </p>
092 *
093 * <pre>
094 * For example:
095 * </pre>
096 *
097 * <pre>
098 * &lt;module name=&quot;CustomImportOrder&quot;&gt;
099 *    &lt;property name=&quot;customImportOrderRules&quot;
100 *    value=&quot;STATIC###SAME_PACKAGE(3)###THIRD_PARTY_PACKAGE###STANDARD_JAVA_PACKAGE&quot;/&gt;
101 *    &lt;property name=&quot;thirdPartyPackageRegExp&quot; value=&quot;com|org&quot;/&gt;
102 *    &lt;property name=&quot;standardPackageRegExp&quot; value=&quot;java|javax&quot;/&gt;
103 * &lt;/module&gt;
104 * </pre>
105 * <p>
106 * Also, this check can be configured to force empty line separator between
107 * import groups. For example
108 * </p>
109 *
110 * <pre>
111 * &lt;module name=&quot;CustomImportOrder&quot;&gt;
112 *    &lt;property name=&quot;separateLineBetweenGroups&quot; value=&quot;true&quot;/&gt;
113 * &lt;/module&gt;
114 * </pre>
115 * <p>
116 * By the option it is possible to force alphabetically sorting.
117 * </p>
118 *
119 * <pre>
120 * &lt;module name=&quot;CustomImportOrder&quot;&gt;
121 *    &lt;property name=&quot;sortImportsInGroupAlphabetically&quot; value=&quot;true&quot;/&gt;
122 * &lt;/module&gt;
123 * </pre>
124 * @author maxvetrenko
125 */
126public class CustomImportOrderCheck extends Check
127{
128
129    /** STATIC group name */
130    private static final String STATIC_RULE_GROUP = "STATIC";
131
132    /** SAME_PACKAGE group name */
133    private static final String SAME_PACKAGE_RULE_GROUP = "SAME_PACKAGE";
134
135    /** THIRD_PARTY_PACKAGE group name */
136    private static final String THIRD_PARTY_PACKAGE_RULE_GROUP = "THIRD_PARTY_PACKAGE";
137
138    /** STANDARD_JAVA_PACKAGE group name */
139    private static final String STANDARD_JAVA_PACKAGE_RULE_GROUP = "STANDARD_JAVA_PACKAGE";
140
141    /** NON_GROUP group name */
142    private static final String SPECIAL_IMPORTS_RULE_GROUP = "SPECIAL_IMPORTS";
143
144    /** NON_GROUP group name */
145    private static final String NON_GROUP_RULE_GROUP = "NON_GROUP";
146
147    /** RegExp for SAME_PACKAGE group imports */
148    private String mSamePackageDomainsRegExp = "";
149
150    /** RegExp for STANDARD_JAVA_PACKAGE group imports */
151    private Pattern mStandardPackageRegExp = Utils.getPattern("java|javax");
152
153    /** RegExp for THIRDPARTY_PACKAGE group imports */
154    private Pattern mThirdPartyPackageRegExp = Utils.getPattern("^$");
155
156    /** RegExp for SPECIAL_IMPORTS group imports */
157    private Pattern mSpecialImportsRegExp = Utils.getPattern("^$");
158
159    /** Force empty line separator between import groups */
160    private boolean mSeparateLineBetweenGroups = true;
161
162    /** Force grouping alphabetically */
163    private boolean mSortImportsInGroupAlphabetically;
164
165    /** List of order declaration customizing by user */
166    private final List<String> mCustomImportOrderRules =
167            new ArrayList<String>();
168
169    /** Number of first domains for SAME_PACKAGE group. */
170    private int mSamePackageMatchingDepth = 2;
171
172    /** Contains objects with import attributes */
173    private List<ImportDetails> mImportToGroupList =
174            new ArrayList<CustomImportOrderCheck.ImportDetails>();
175
176    /**
177     * Sets mStandardRegExp specified by user.
178     * @param aRegexp
179     *        user value.
180     */
181    public final void setStandardPackageRegExp(String aRegexp)
182    {
183        mStandardPackageRegExp = Utils.getPattern(aRegexp);
184    }
185
186    /**
187     * Sets mThirdPartyRegExp specified by user.
188     * @param aRegexp
189     *        user value.
190     */
191    public final void setThirdPartyPackageRegExp(String aRegexp)
192    {
193        mThirdPartyPackageRegExp = Utils.getPattern(aRegexp);
194    }
195
196    /**
197     * Sets mSpecialImportsRegExp specified by user.
198     * @param aRegexp
199     *        user value.
200     */
201    public final void setSpecialImportsRegExp(String aRegexp)
202    {
203        mSpecialImportsRegExp = Utils.getPattern(aRegexp);
204    }
205
206    /**
207     * Sets mSeparateLineBetweenGroups specified by user.
208     * @param aValue
209     *        user value.
210     */
211    public final void setSeparateLineBetweenGroups(boolean aValue)
212    {
213        mSeparateLineBetweenGroups = aValue;
214    }
215
216    /**
217     * Sets mSortImportsInGroupAlphabetically specified by user.
218     * @param aValue
219     *        user value.
220     */
221    public final void setSortImportsInGroupAlphabetically(boolean aValue)
222    {
223        mSortImportsInGroupAlphabetically = aValue;
224    }
225
226    /**
227     * Sets a custom import order from the rules in the string format specified
228     * by user.
229     * @param aInputCustomImportOrder
230     *        user value.
231     */
232    public final void setCustomImportOrderRules(final String aInputCustomImportOrder)
233    {
234        mCustomImportOrderRules.clear();
235        try {
236            for (String currentState : aInputCustomImportOrder
237                    .split("\\s*###\\s*"))
238            {
239                addRulesToList(currentState);
240            }
241            mCustomImportOrderRules.add(NON_GROUP_RULE_GROUP);
242        }
243        catch (StringIndexOutOfBoundsException exp) {
244            //if the structure of the input rule isn't correct
245            throw new RuntimeException("Unable to parse input rule: " + exp);
246        }
247    }
248
249    @Override
250    public int[] getDefaultTokens()
251    {
252        return new int[] {
253            TokenTypes.IMPORT,
254            TokenTypes.STATIC_IMPORT,
255            TokenTypes.PACKAGE_DEF,
256        };
257    }
258
259    @Override
260    public void beginTree(DetailAST aRootAST)
261    {
262        mImportToGroupList.clear();
263    }
264
265    @Override
266    public void visitToken(DetailAST aAST)
267    {
268        if (aAST.getType() == TokenTypes.PACKAGE_DEF) {
269            if (mCustomImportOrderRules.contains(SAME_PACKAGE_RULE_GROUP)
270                    && mSamePackageMatchingDepth != -1)
271            {
272                mSamePackageDomainsRegExp = createSamePackageRegexp(
273                        mSamePackageMatchingDepth, aAST);
274            }
275        }
276        else {
277            final String importFullPath = getFullImportIdent(aAST);
278            final int lineNo = aAST.getLineNo();
279            final boolean isStatic = aAST.getType() == TokenTypes.STATIC_IMPORT;
280            mImportToGroupList.add(new ImportDetails(importFullPath,
281                    lineNo, getImportGroup(isStatic, importFullPath),
282                    isStatic));
283        }
284    }
285
286    @Override
287    public void finishTree(DetailAST aRootAST)
288    {
289
290        if (mImportToGroupList.isEmpty()) {
291            return;
292        }
293
294        final ImportDetails firstImport = mImportToGroupList.get(0);
295        String currentGroup = getImportGroup(firstImport.isStatic(),
296                firstImport.getImportFullPath());
297        int groupNumber = mCustomImportOrderRules.indexOf(currentGroup);
298        String previousImport = null;
299
300        for (ImportDetails importObject : mImportToGroupList) {
301            final String importGroup = importObject.getImportGroup();
302            final String fullImportIdent = importObject.mImportFullPath;
303
304            if (!importGroup.equals(currentGroup)) {
305                if (mCustomImportOrderRules.size() > groupNumber + 1) {
306                    final String nextGroup = getNextImportGroup(groupNumber + 1);
307                    if (importGroup.equals(nextGroup)) {
308                        if (mSeparateLineBetweenGroups && previousImport != null
309                                && !hasEmptyLineBefore(importObject.getLineNumber()))
310                        {
311                            log(importObject.getLineNumber(), "custom.import.order.line.separator",
312                                    fullImportIdent);
313                        }
314                        currentGroup = nextGroup;
315                        groupNumber = mCustomImportOrderRules.indexOf(nextGroup);
316                    }
317                    else {
318                        logWrongImportGroupOrder(importObject.getLineNumber(),
319                                importGroup);
320                    }
321                }
322                else {
323                    logWrongImportGroupOrder(importObject.getLineNumber(),
324                            importGroup);
325                }
326            }
327            else if (mSortImportsInGroupAlphabetically
328                    && previousImport != null
329                    && matchesImportGroup(importObject.isStatic(),
330                            fullImportIdent, currentGroup)
331                    && !(compare(fullImportIdent, previousImport) >= 0))
332            {
333                log(importObject.getLineNumber(), "custom.import.order.lex", fullImportIdent);
334            }
335            previousImport = fullImportIdent;
336        }
337    }
338
339    /**
340     * Log wrong import group order.
341     * @param aCurrentImportLine
342     *        line number of current import current import.
343     * @param aImportGroup
344     *        import group.
345     */
346    private void logWrongImportGroupOrder(int aCurrentImportLine, String aImportGroup)
347    {
348        if (NON_GROUP_RULE_GROUP.equals(aImportGroup)) {
349            log(aCurrentImportLine, "custom.import.order.nongroup.import");
350        }
351        else {
352            log(aCurrentImportLine, "custom.import.order", aImportGroup);
353        }
354    }
355
356    /**
357     * Get next import group.
358     * @param aCurrentGroupNumber
359     *        current group number.
360     * @return
361     *        next import group.
362     */
363    private String getNextImportGroup(int aCurrentGroupNumber)
364    {
365        int nextGroupNumber = aCurrentGroupNumber;
366
367        while (mCustomImportOrderRules.size() > nextGroupNumber + 1) {
368            if (hasAnyImportInCurrentGroup(mCustomImportOrderRules.get(nextGroupNumber)))
369            {
370                break;
371            }
372            nextGroupNumber++;
373        }
374        return mCustomImportOrderRules.get(nextGroupNumber);
375    }
376
377    /**
378     * Checks if current group contains any import.
379     * @param aCurrentGroup
380     *        current group.
381     * @return
382     *        true, if current group contains at least one import.
383     */
384    private boolean hasAnyImportInCurrentGroup(String aCurrentGroup)
385    {
386        for (ImportDetails currentImport : mImportToGroupList) {
387            if (aCurrentGroup.equals(currentImport.getImportGroup())) {
388                return true;
389            }
390        }
391        return false;
392    }
393
394    /**
395     * Get import valid group.
396     * @param aStatic
397     *        is static import.
398     * @param aImportPath
399     *        full import path.
400     * @return import valid group.
401     */
402    private String getImportGroup(boolean aStatic, String aImportPath)
403    {
404        for (String group : mCustomImportOrderRules) {
405            if (matchesImportGroup(aStatic, aImportPath, group)) {
406                return group;
407            }
408        }
409        return NON_GROUP_RULE_GROUP;
410    }
411
412    /**
413     * Checks if the import is placed in the correct group.
414     * @param aStatic
415     *        if import is static.
416     * @param aImportPath
417     *        import full path.
418     * @param aCurrentGroup
419     *        current group.
420     * @return true, if import placed in the correct group.
421     */
422    private boolean matchesImportGroup(boolean aStatic, String aImportPath, String aCurrentGroup)
423    {
424        return matchesStaticImportGroup(aStatic, aCurrentGroup)
425                || matchesSamePackageImportGroup(aStatic, aImportPath, aCurrentGroup)
426                || matchesSpecialImportsGroup(aStatic, aImportPath, aCurrentGroup)
427                || matchesStandartImportGroup(aStatic, aImportPath, aCurrentGroup)
428                || matchesThirdPartyImportGroup(aStatic, aImportPath, aCurrentGroup);
429    }
430
431    /**
432     * Checks if the import is placed in the STATIC group.
433     * @param aStatic
434     *        is static import.
435     * @param aCurrentGroup
436     *        current group.
437     * @return true, if the import is placed in the static group.
438     */
439    private boolean matchesStaticImportGroup(boolean aStatic, String aCurrentGroup)
440    {
441        return aStatic && STATIC_RULE_GROUP.equals(aCurrentGroup);
442    }
443
444    /**
445     * Checks if the import is placed in the correct group.
446     * @param aStatic
447     *        if import is static.
448     * @param aImportPath
449     *        import full path.
450     * @param aCurrentGroup
451     *        current group.
452     * @return true, if the import is placed in the static group.
453     */
454    private boolean matchesSamePackageImportGroup(boolean aStatic,
455        String aImportPath, String aCurrentGroup)
456    {
457        final String importPath = aImportPath.substring(0, aImportPath.lastIndexOf("."));
458        return !aStatic && SAME_PACKAGE_RULE_GROUP.equals(aCurrentGroup)
459                && mSamePackageDomainsRegExp.contains(importPath);
460    }
461
462    /**
463     * Checks if the import is placed in the correct group.
464     * @param aStatic
465     *        if import is static.
466     * @param aCurrentImport
467     *        import full path.
468     * @param aCurrentGroup
469     *        current group.
470     * @return true, if the import is placed in the static group.
471     */
472    private boolean matchesStandartImportGroup(boolean aStatic,
473        String aCurrentImport, String aCurrentGroup)
474    {
475        return !aStatic && STANDARD_JAVA_PACKAGE_RULE_GROUP.equals(aCurrentGroup)
476                && mStandardPackageRegExp.matcher(aCurrentImport).find();
477    }
478
479    /**
480     * Checks if the import is placed in the correct group.
481     * @param aStatic
482     *        if import is static.
483     * @param aCurrentImport
484     *        import full path.
485     * @param aCurrentGroup
486     *        current group.
487     * @return true, if the import is placed in the static group.
488     */
489    private boolean matchesSpecialImportsGroup(boolean aStatic,
490        String aCurrentImport, String aCurrentGroup)
491    {
492        return !aStatic && SPECIAL_IMPORTS_RULE_GROUP.equals(aCurrentGroup)
493                && mSpecialImportsRegExp.matcher(aCurrentImport).find();
494    }
495
496    /**
497     * Checks if the import is placed in the correct group.
498     * @param aStatic
499     *        if import is static.
500     * @param aCurrentImport
501     *        import full path.
502     * @param aCurrentGroup
503     *        current group.
504     * @return true, if the import is placed in the static group.
505     */
506    private boolean matchesThirdPartyImportGroup(boolean aStatic,
507        String aCurrentImport, String aCurrentGroup)
508    {
509        return !aStatic && THIRD_PARTY_PACKAGE_RULE_GROUP.equals(aCurrentGroup)
510                && mThirdPartyPackageRegExp.matcher(aCurrentImport).find()
511                && !mStandardPackageRegExp.matcher(aCurrentImport).find();
512    }
513
514    /**
515     * Checks compare two import paths.
516     * @param aCurrentImport
517     *        current import.
518     * @param aPreviousImport
519     *        previous import.
520     * @return a negative integer, zero, or a positive integer as the
521     *        specified String is greater than, equal to, or less
522     *        than this String, ignoring case considerations.
523     */
524    private int compare(String aCurrentImport, String aPreviousImport)
525    {
526        int indexOfPreviousDotCurrent = 0;
527        int indexOfNextDotCurrent = 0;
528        String tokenCurrent = "";
529        int indexOfPreviousDotPrevious = 0;
530        int indexOfNextDotPrevious = 0;
531        String tokenPrevious = "";
532        final int currentImportDomainCount = countDomains(aCurrentImport);
533        final int previousImportDomainCount = countDomains(aPreviousImport);
534        int result = 0;
535
536        while (aCurrentImport.lastIndexOf(".") != indexOfPreviousDotCurrent - 1
537                && aPreviousImport.lastIndexOf(".") != indexOfPreviousDotPrevious - 1)
538        {
539            indexOfNextDotCurrent = aCurrentImport.indexOf(".", indexOfPreviousDotCurrent + 1);
540            indexOfNextDotPrevious = aPreviousImport.indexOf(".", indexOfPreviousDotPrevious + 1);
541            tokenCurrent = aCurrentImport.substring(indexOfPreviousDotCurrent,
542                    indexOfNextDotCurrent);
543            tokenPrevious = aPreviousImport.substring(indexOfPreviousDotPrevious,
544                    indexOfNextDotPrevious);
545            result = tokenCurrent.compareToIgnoreCase(tokenPrevious);
546            if (result != 0) {
547                return result;
548            }
549            indexOfPreviousDotCurrent = indexOfNextDotCurrent + 1;
550            indexOfPreviousDotPrevious = indexOfNextDotPrevious + 1;
551        }
552
553        if (result == 0 && (aCurrentImport.lastIndexOf(".") == indexOfPreviousDotCurrent - 1
554                || aPreviousImport.lastIndexOf(".") == indexOfPreviousDotPrevious - 1))
555        {
556            if (currentImportDomainCount != previousImportDomainCount) {
557                getClassName(indexOfNextDotPrevious, aPreviousImport);
558                return currentImportDomainCount - previousImportDomainCount;
559            }
560            else {
561                getClassName(indexOfNextDotPrevious, aPreviousImport);
562                return getClassName(indexOfNextDotCurrent,
563                        aCurrentImport).compareToIgnoreCase(getClassName(indexOfNextDotPrevious,
564                                aPreviousImport));
565            }
566        }
567        return 0;
568    }
569
570    /**
571     * Return class name from import full path.
572     * @param aStartFrom number of start.
573     * @param aImport import full path.
574     * @return class name.
575     */
576    private String getClassName(int aStartFrom, String aImport)
577    {
578        String className = aImport;
579        className = className.substring(aStartFrom, className.length());
580        final StringTokenizer token = new StringTokenizer(className, ".\r");
581        return token.nextToken();
582    }
583
584    /**
585     * Count number of domains.
586     * @param aImportPath current import.
587     * @return number of domains.
588     */
589    private static int countDomains(String aImportPath)
590    {
591        final StringTokenizer tokens = new StringTokenizer(aImportPath, ".");
592        int count = 0;
593
594        while (tokens.hasMoreTokens()) {
595            if (!Character.isUpperCase(tokens.nextToken().toString().charAt(0))) {
596                count++;
597            }
598            else {
599                break;
600            }
601        }
602        return count - 1;
603    }
604
605    /**
606     * Checks if a token has a empty line before.
607     * @param aLineNo
608     *        Line number of current import.
609     * @return true, if token have empty line before.
610     */
611    private boolean hasEmptyLineBefore(int aLineNo)
612    {
613        //  [lineNo - 2] is the number of the previous line
614        //  because the numbering starts from zero.
615        final String lineBefore = getLines()[aLineNo - 2];
616        return lineBefore.trim().isEmpty();
617    }
618
619    /**
620     * Forms import full path.
621     * @param aToken
622     *        current token.
623     * @return full path or null.
624     */
625    private static String getFullImportIdent(DetailAST aToken)
626    {
627        return aToken != null ? FullIdent.createFullIdent(aToken.
628                findFirstToken(TokenTypes.DOT)).getText() : "";
629    }
630
631    /**
632     * Parses ordering rule and adds it to the list with rules.
633     * @param aRule
634     *        String with rule.
635     */
636    private void addRulesToList(String aRule)
637    {
638        if (STATIC_RULE_GROUP.equals(aRule)
639                || THIRD_PARTY_PACKAGE_RULE_GROUP.equals(aRule)
640                || STANDARD_JAVA_PACKAGE_RULE_GROUP.equals(aRule)
641                || SPECIAL_IMPORTS_RULE_GROUP.equals(aRule))
642        {
643            mCustomImportOrderRules.add(aRule);
644
645        }
646        else if (aRule.startsWith(SAME_PACKAGE_RULE_GROUP)) {
647
648            final String rule = aRule.substring(aRule.indexOf("(") + 1,
649                    aRule.indexOf(")"));
650            try {
651                mSamePackageMatchingDepth = Integer.parseInt(rule);
652            }
653            catch (Exception e) {
654                mSamePackageDomainsRegExp = rule;
655            }
656            mCustomImportOrderRules.add(SAME_PACKAGE_RULE_GROUP);
657
658        }
659        else {
660            throw new RuntimeException("Unexpected rule: " + aRule);
661        }
662    }
663
664    /**
665     * Creates mSamePackageDomainsRegExp of the first package domains.
666     * @param aCount
667     *        number of first package domains.
668     * @param aPackageNode
669     *        package node.
670     * @return same package regexp.
671     */
672    private static String createSamePackageRegexp(int aCount, DetailAST aPackageNode)
673    {
674        final StringBuilder builder = new StringBuilder();
675        final String packageFullPath = getFullImportIdent(aPackageNode);
676        final StringTokenizer tokens = new StringTokenizer(packageFullPath, ".");
677        int count = aCount;
678
679        while (tokens.hasMoreTokens() && count > 0) {
680            builder.append(tokens.nextToken()).append(".");
681            count--;
682        }
683        return builder.append("*").toString();
684    }
685
686    /**
687     * Contains import attributes as line number, import full path, import
688     * group.
689     * @author max
690     */
691    class ImportDetails
692    {
693        /** Import full path */
694        private String mImportFullPath;
695
696        /** Import line number */
697        private int mLineNumber;
698
699        /** Import group */
700        private String mImportGroup;
701
702        /** Is static import */
703        private boolean mStatic;
704
705        /**
706         * @param aImportFullPath
707         *        import full path.
708         * @param aLineNumber
709         *        import line number.
710         * @param aImportGroup
711         *        import group.
712         * @param aStatic
713         *        if import is static.
714         */
715        public ImportDetails(String aImportFullPath,
716                int aLineNumber, String aImportGroup, boolean aStatic)
717        {
718            setImportFullPath(aImportFullPath);
719            setLineNumber(aLineNumber);
720            setImportGroup(aImportGroup);
721            setStatic(aStatic);
722        }
723
724        /**
725         * Get import full path variable.
726         * @return import full path variable.
727         */
728        public String getImportFullPath()
729        {
730            return mImportFullPath;
731        }
732
733        /**
734         * Set import full path variable.
735         * @param aImportFullPath
736         *        import full path variable.
737         */
738        public void setImportFullPath(String aImportFullPath)
739        {
740            this.mImportFullPath = aImportFullPath;
741        }
742
743        /**
744         * Get import line number.
745         * @return import line.
746         */
747        public int getLineNumber()
748        {
749            return mLineNumber;
750        }
751
752        /**
753         * Set import line number.
754         * @param aLineNumber
755         *        import line number.
756         */
757        public void setLineNumber(int aLineNumber)
758        {
759            this.mLineNumber = aLineNumber;
760        }
761
762        /**
763         * Get import group.
764         * @return import group.
765         */
766        public String getImportGroup()
767        {
768            return mImportGroup;
769        }
770
771        /**
772         * Set import group.
773         * @param aImportGroup
774         *        import group.
775         */
776        public void setImportGroup(String aImportGroup)
777        {
778            this.mImportGroup = aImportGroup;
779        }
780
781        /**
782         * Checks if import is static.
783         * @return true, if import is static.
784         */
785        public boolean isStatic()
786        {
787            return mStatic;
788        }
789
790        /**
791         * Set true, if import is static
792         * @param aStatic
793         *        if import is static.
794         */
795        public void setStatic(boolean aStatic)
796        {
797            this.mStatic = aStatic;
798        }
799    }
800}