From a5d30bf353dde01376d45fb031d9236b11a835d1 Mon Sep 17 00:00:00 2001 From: Oliver Burn Date: Sun, 31 Jul 2005 12:27:44 +0000 Subject: [PATCH] Added a lot more configuration options for the ImportControlCheck that came out of using at work. I still need to improve the unit tests and properly document the support options. Wanted to check in now for backup. --- import-control.xml | 28 ++---- .../checkstyle/checks/imports/Guard.java | 87 ++++++++++++++++--- .../checks/imports/ImportControlCheck.java | 9 +- .../checks/imports/ImportControlLoader.java | 24 ++++- .../checkstyle/checks/imports/PkgControl.java | 21 +++-- .../checks/imports/import_control_1_0.dtd | 14 +-- .../checkstyle/checks/imports/GuardTest.java | 37 ++++++-- .../checks/imports/PkgControlTest.java | 32 +++---- 8 files changed, 185 insertions(+), 67 deletions(-) diff --git a/import-control.xml b/import-control.xml index af553fe9e..db12c3902 100755 --- a/import-control.xml +++ b/import-control.xml @@ -15,23 +15,24 @@ - - - - - + + + + + - + + - - @@ -42,25 +43,14 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/Guard.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/Guard.java index 3432ca09b..368b1a688 100755 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/Guard.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/Guard.java @@ -28,29 +28,96 @@ class Guard private final boolean mAllowed; /** Package to control access to. */ private final String mPkgName; + /** Package to control access to. */ + private final String mClassName; + + /** + * Indicates if must be an exact match. Only valid if guard using a + * package. + */ + private final boolean mExactMatch; + /** Indicates if the guard only applies to this package. */ + private final boolean mLocalOnly; /** * Constructs an instance. - * @param aAllowed whether the package is allowed. - * @param aPkgName the package name. + * @param aAllow whether to allow access. + * @param aLocalOnly whether guard is to be applied locally only + * @param aPkgName the package to apply guard on. + * @param aExactMatch whether the package must match exactly. */ - Guard(final boolean aAllowed, final String aPkgName) + Guard(final boolean aAllow, final boolean aLocalOnly, + final String aPkgName, final boolean aExactMatch) { - mAllowed = aAllowed; + mAllowed = aAllow; + mLocalOnly = aLocalOnly; mPkgName = aPkgName; + mClassName = null; + mExactMatch = aExactMatch; + } + + /** + * Constructs an instance. + * @param aAllow whether to allow access. + * @param aLocalOnly whether guard is to be applied locally only + * @param aClassName the class to apply guard on. + */ + Guard(final boolean aAllow, final boolean aLocalOnly, + final String aClassName) + { + mAllowed = aAllow; + mLocalOnly = aLocalOnly; + mPkgName = null; + mClassName = aClassName; + mExactMatch = true; // not used. } /** * Verifies whether a package name be used. - * @param aName the package to check. + * @param aForImport the package to check. + * @param aInPkg the package doing the import. * @return a result {@link AccessResult} indicating whether it can be used. */ - AccessResult verify(final String aName) + AccessResult verifyImport(final String aForImport, final String aInPkg) { - assert aName != null; - if (!aName.startsWith(mPkgName + ".")) { - return AccessResult.UNKNOWN; + assert aForImport != null; + if (mClassName != null) { + final boolean classMatch = mClassName.equals(aForImport); + return calculateResult(classMatch); } - return mAllowed ? AccessResult.ALLOWED : AccessResult.DISALLOWED; + + // Must be checking a package. First check that we actually match + // the package. Then check if matched and we must be an exact match. + // In this case, the text after the first "." must not contain + // another "." as this indicates that it is not an exact match. + assert mPkgName != null; + //boolean pkgMatch = aForImport.startsWith(mPkgName + "."); + boolean pkgMatch = aForImport.startsWith(mPkgName + "."); + if (pkgMatch && mExactMatch) { + pkgMatch = (aForImport.indexOf('.', (mPkgName.length() + 1)) == -1); + } + return calculateResult(pkgMatch); + } + + /** + * @return returns whether the guard is to only be applied locally. + */ + boolean isLocalOnly() + { + return mLocalOnly; + } + + /** + * Returns the appropriate {@link AccessResult} based on whether there + * was a match and if the guard is to allow access. + * @param aMatched indicates whether there was a match. + * @return An appropriate {@link AccessResult}. + */ + private AccessResult calculateResult(final boolean aMatched) + { + if (aMatched) { + return mAllowed ? AccessResult.ALLOWED : AccessResult.DISALLOWED; + } + return AccessResult.UNKNOWN; } } diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlCheck.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlCheck.java index 64f73e03a..e03a56fee 100755 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlCheck.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlCheck.java @@ -52,6 +52,9 @@ public class ImportControlCheck extends Check { /** The root package controller. */ private PkgControl mRoot; + /** The package doing the import. */ + private String mInPkg; + /** * The package controller for the current file. Used for performance * optimisation. @@ -75,7 +78,8 @@ public class ImportControlCheck extends Check log(nameAST, "import.control.missing.file"); } else { - mCurrentLeaf = mRoot.locateFinest(full.getText()); + mInPkg = full.getText(); + mCurrentLeaf = mRoot.locateFinest(mInPkg); } } else if (mCurrentLeaf != null) { @@ -87,7 +91,8 @@ public class ImportControlCheck extends Check imp = FullIdent.createFullIdent((DetailAST) aAST .getFirstChild().getNextSibling()); } - final AccessResult access = mCurrentLeaf.checkAccess(imp.getText()); + final AccessResult access = mCurrentLeaf.checkAccess(imp.getText(), + mInPkg); if (!AccessResult.ALLOWED.equals(access)) { log(aAST, "import.control.disallowed", imp.getText()); } diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlLoader.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlLoader.java index 23e2d5d89..d316277ca 100755 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlLoader.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/ImportControlLoader.java @@ -59,7 +59,9 @@ final class ImportControlLoader extends AbstractLoader /** {@inheritDoc} */ public void startElement(final String aNamespaceURI, - final String aLocalName, final String aQName, final Attributes aAtts) + final String aLocalName, + final String aQName, + final Attributes aAtts) throws SAXException { if (aQName.equals("import-control")) { @@ -73,9 +75,25 @@ final class ImportControlLoader extends AbstractLoader } else if (aQName.equals("allow") || aQName.equals("disallow")) { assert mStack.size() > 0; - final String pkg = safeGet(aAtts, "pkg"); + // Need to handle either "pkg" or "class" attribute. + // May have "exact-match" for "pkg" + // May have "local-only" + final boolean isAllow = aQName.equals("allow"); + final boolean isLocalOnly = (aAtts.getValue("local-only") != null); + final String pkg = aAtts.getValue("pkg"); + final Guard g; + if (pkg != null) { + final boolean exactMatch = + (aAtts.getValue("exact-match") != null); + g = new Guard(isAllow, isLocalOnly, pkg, exactMatch); + } + else { + final String clazz = safeGet(aAtts, "class"); + g = new Guard(isAllow, isLocalOnly, clazz); + } + final PkgControl pc = (PkgControl) mStack.peek(); - pc.addGuard(new Guard(aQName.equals("allow"), pkg)); + pc.addGuard(g); } } diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/PkgControl.java b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/PkgControl.java index 48b475ed2..82aa6f175 100755 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/PkgControl.java +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/PkgControl.java @@ -117,12 +117,13 @@ class PkgControl * its parent looking for a match. This will recurse looking for match. * If there is no clear result then {@link AccessResult#UNKNOWN} is * returned. - * @param aForPkg the package to check on. + * @param aForImport the package to check on. + * @param aInPkg the package doing the import. * @return an {@link AccessResult}. */ - AccessResult checkAccess(final String aForPkg) + AccessResult checkAccess(final String aForImport, final String aInPkg) { - AccessResult retVal = localCheckAccess(aForPkg); + AccessResult retVal = localCheckAccess(aForImport, aInPkg); if (retVal != AccessResult.UNKNOWN) { return retVal; } @@ -131,21 +132,27 @@ class PkgControl return AccessResult.DISALLOWED; } - return mParent.checkAccess(aForPkg); + return mParent.checkAccess(aForImport, aInPkg); } /** * Checks whether any of the guards for this node control access to * a specified package. - * @param aForPkg the package to check. + * @param aForImport the package to check. + * @param aInPkg the package doing the import. * @return an {@link AccessResult}. */ - private AccessResult localCheckAccess(final String aForPkg) + private AccessResult localCheckAccess(final String aForImport, + final String aInPkg) { final Iterator it = mGuards.iterator(); while (it.hasNext()) { final Guard g = (Guard) it.next(); - final AccessResult result = g.verify(aForPkg); + // Check if a Guard is only meant to be applied locally. + if (g.isLocalOnly() && !mFullPackage.equals(aInPkg)) { + continue; + } + final AccessResult result = g.verifyImport(aForImport, aInPkg); if (result != AccessResult.UNKNOWN) { return result; } diff --git a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/import_control_1_0.dtd b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/import_control_1_0.dtd index 9f9ecd2c5..102957db8 100755 --- a/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/import_control_1_0.dtd +++ b/src/checkstyle/com/puppycrawl/tools/checkstyle/checks/imports/import_control_1_0.dtd @@ -1,4 +1,4 @@ - + @@ -10,12 +10,16 @@ - + + + %attlist.guard;> - + %attlist.guard;> diff --git a/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/GuardTest.java b/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/GuardTest.java index 06884cc34..3fe207279 100755 --- a/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/GuardTest.java +++ b/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/GuardTest.java @@ -6,13 +6,38 @@ import junit.framework.TestCase; public class GuardTest extends TestCase { - public void testGuard() + public void testPkgGuard1() { - final Guard g = new Guard(true, "pkg"); + final Guard g = new Guard(true, false, "pkg", false); assertNotNull(g); - assertEquals(AccessResult.UNKNOWN, g.verify("asda")); - assertEquals(AccessResult.UNKNOWN, g.verify("p")); - assertEquals(AccessResult.ALLOWED, g.verify("pkg.a")); - assertEquals(AccessResult.UNKNOWN, g.verify("pkg")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("asda", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("p", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("pkga", "ignored")); + assertEquals(AccessResult.ALLOWED, g.verifyImport("pkg.a", "ignored")); + assertEquals(AccessResult.ALLOWED, g.verifyImport("pkg.a.b", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("pkg", "ignored")); + } + + public void testPkgGuard2() + { + final Guard g = new Guard(true, false, "pkg", true); + assertNotNull(g); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("asda", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("p", "ignored")); + assertEquals(AccessResult.ALLOWED, g.verifyImport("pkg.a", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("pkg.a.b", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("pkg", "ignored")); + } + + public void testClassGuard() + { + final Guard g = new Guard(true, false, "pkg.a"); + assertNotNull(g); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("asda", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("p", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("pkga", "ignored")); + assertEquals(AccessResult.ALLOWED, g.verifyImport("pkg.a", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("pkg.a.b", "ignored")); + assertEquals(AccessResult.UNKNOWN, g.verifyImport("pkg", "ignored")); } } diff --git a/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/PkgControlTest.java b/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/PkgControlTest.java index 33c4b9c65..5323e34ea 100755 --- a/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/PkgControlTest.java +++ b/src/tests/com/puppycrawl/tools/checkstyle/checks/imports/PkgControlTest.java @@ -13,11 +13,11 @@ public class PkgControlTest extends TestCase protected void setUp() throws Exception { super.setUp(); - pcRoot.addGuard(new Guard(false, "org.springframework")); - pcRoot.addGuard(new Guard(false, "org.hibernate")); - pcRoot.addGuard(new Guard(true, "org.apache.commons")); + pcRoot.addGuard(new Guard(false, false, "org.springframework", false)); + pcRoot.addGuard(new Guard(false, false, "org.hibernate", false)); + pcRoot.addGuard(new Guard(true, false, "org.apache.commons", false)); - pcCommon.addGuard(new Guard(true, "org.hibernate")); + pcCommon.addGuard(new Guard(true, false, "org.hibernate", false)); } public void testFullPkg() @@ -37,17 +37,19 @@ public class PkgControlTest extends TestCase public void testCheckAccess() { - assertEquals(AccessResult.DISALLOWED, pcCommon - .checkAccess("org.springframework.something")); + assertEquals(AccessResult.DISALLOWED, pcCommon.checkAccess( + "org.springframework.something", + "com.kazgroup.courtlink.common")); assertEquals(AccessResult.ALLOWED, pcCommon - .checkAccess("org.apache.commons.something")); - assertEquals(AccessResult.DISALLOWED, pcCommon - .checkAccess("org.apache.commons")); - assertEquals(AccessResult.ALLOWED, pcCommon - .checkAccess("org.hibernate.something")); - assertEquals(AccessResult.DISALLOWED, pcCommon - .checkAccess("com.badpackage.something")); - assertEquals(AccessResult.DISALLOWED, pcRoot - .checkAccess("org.hibernate.something")); + .checkAccess("org.apache.commons.something", + "com.kazgroup.courtlink.common")); + assertEquals(AccessResult.DISALLOWED, pcCommon.checkAccess( + "org.apache.commons", "com.kazgroup.courtlink.common")); + assertEquals(AccessResult.ALLOWED, pcCommon.checkAccess( + "org.hibernate.something", "com.kazgroup.courtlink.common")); + assertEquals(AccessResult.DISALLOWED, pcCommon.checkAccess( + "com.badpackage.something", "com.kazgroup.courtlink.common")); + assertEquals(AccessResult.DISALLOWED, pcRoot.checkAccess( + "org.hibernate.something", "com.kazgroup.courtlink")); } }