Macker Guide

Variables and For Each

Variables

If a certain string appears often in your regular expressions, you can assign it to a variable. A variable declaration has a very simple syntax:

<var name="var-name" value="string" />

You can refer to a variable in any pattern or message using a shell-style syntax:

${var-name}

For example, you could define a variable for your application's base package, then use it in your patterns:

<var name="app-base" value="com.foobar.whatsitproject" />
<pattern name="ui" class="${app-base}.ui.**" />
<pattern name="db" class="${app-base}.db.**" />

As with patterns, the scope of a variable is the ruleset or foreach element which contains it. (You can also define a global variable when you call Macker; see the Ant task docs.)

An access rule defines six special variables, which you can use in patterns in the access rule, and in its message.
${from} The name of the class matching <from>, without the package name
${to} The name of the class matching <to>, without the package name
${from-package} The package name of the <from> class
${to-package} The package name of the <to> class
${from-full} The fully qualified (including the package) name of the <from> class
${to-full} The fully qualified (including the package) name of the <to> class

Messages

You can print arbitrary messages during the execution of a rules file.

<message>message text</message>

Messages may contain variables.

<message>Commencing error checking for ${base.package}...</message>

Messages are actually a kind of rule, and like access rules, they have severities. The severity of a message is info by default. You can use the debug severity to hide a message during normal execution, which makes messages useful for tracing variable values:

<message severity="debug">var1=${var1} var2=${var2}</message>

You can even make a message cause rule checking to fail by using the error severity, if that really seems useful to you.

For Each

Astute observers will note that, up to this point, patterns and rules always apply to "any X" -- it is possible to say things like "any logic class may access any data object", but not to say "any logic class may access its own data object". The Macker foreach construct provides a way of doing this.

The <foreach> tag iterates a variable over all the matches of a given pattern in the names of the primary classes and their direct dependencies. The tag takes a variable name and a class regex, which may optionally contain a set of parentheses.

<foreach var="var-name" class="var-value)" />
<foreach var="var-name" class="...(var-value)..." />

For the matching classes, the variable will iterate over the substrings falling inside the parentheses. If the foreach matches several different classes for which the parentheses enclose the same substring, that substring only appears once. If there are no parentheses, the variable will iterate over the full names of the matching classes.

Confusing? Some examples should make it a bit clearer.
A foreach with this regex......applied to these primary classes......will iterate through these values.
**Exception com.foobar.Thinger
com.foobar.IllegalThingerException
com.foobar.NoSuchThingerException
com.foobar.IllegalThingerException
com.foobar.NoSuchThingerException
(**)Exception com.foobar.Thinger
com.foobar.IllegalThingerException
com.foobar.NoSuchThingerException
com.foobar.IllegalThinger
com.foobar.NoSuchThinger
**.(*Exception) com.foobar.Thinger
com.foobar.IllegalThingerException
com.foobar.NoSuchThingerException
IllegalThingerException
NoSuchThingerException
(**).*Thinger com.foobar.Thinger
com.foobar.limb.ArmThinger
com.foobar.limb.LegThinger
com.foobar.limb.TentacleThinger
com.foobar.mood.HappyThinger
com.foobar.mood.SadThinger
com.foobar
com.foobar.limb
com.foobar.mood

Remember that a regex always matches the entire name of the class. This can give confusing results -- be careful!
A foreach with this regex......applied to these primary classes......will iterate through these values.
com.foobar.*
(One might expect this pattern to match each subpackage of com.foobar, but it doesn't -- it needs to match the full class names.)
com.foobar.Thinger
com.foobar.limb.ArmThinger
com.foobar.limb.LegThinger
com.foobar.limb.TentacleThinger
com.foobar.mood.HappyThinger
com.foobar.mood.SadThinger
com.foobar.Thinger
(com.foobar**).*
(This pattern gives just the package names. It matches the full class names, but leaves the class part outside the parentheses.)
com.foobar.Thinger
com.foobar.limb.ArmThinger
com.foobar.limb.LegThinger
com.foobar.limb.TentacleThinger
com.foobar.mood.HappyThinger
com.foobar.mood.SadThinger
com.foobar
com.foobar.limb
com.foobar.mood
(com.foobar.**).*
(The extra dot rules out com.foobar.Thinger.)
com.foobar.Thinger
com.foobar.limb.ArmThinger
com.foobar.limb.LegThinger
com.foobar.limb.TentacleThinger
com.foobar.mood.HappyThinger
com.foobar.mood.SadThinger
com.foobar.limb
com.foobar.mood

Foreach is a powerful construct. Here, for example, is one possible implementation of the example from the FAQ, "One functional module may access another only through its API." In this hypothetical situation, the application has a set of modules in packages named com.foobar.module.xyz, with implementation classes in subpackages.

<ruleset>
    <var name="base-pkg" value="com.foobar" />
    <foreach var="module" class="(${base-pkg}.module.*).**">

        <pattern name="api" class="${module}.*" />
        <pattern name="inside" class="${module}.**" />
        <pattern name="outside">
            <exclude pattern name="inside" />
        </pattern>

        <access-rule>
            <message>${from} must access the module ${module} through its API</message>
            <deny>
                <from pattern="outside" />
                <to pattern="inside" />
                <allow>
                    <to pattern="api" />
                </allow>
            </deny>
        </access-rule>

    </foreach>
</ruleset>

If you understand this example, you have the mystery of the Macker rules file in hand.

SourceForge Logo        innig