To declare an attribute in your code, add a tag to the Javadoc comment just before the element in question. For example:
/** * Central access point to all attribute-related methods. * * @pattern singleton */ public class Attributes { // ... }
You can add these attributes to any element that can normally have Javadoc comments, including classes, interfaces, fields and methods. You can also continue to use the standard Javadoc tags (e.g. @author, @param, etc.), and they will by default be ignored by the attributes module. (See the compiler section below for details.)
The syntax of each attribute tag can generally be anything you want, though constraints can be imposed depending on the attribute compiler mode. By default, the compiler runs in "string" mode, where each tag is converted into a string key/value pair. The tag's name up to the first whitespace character (pattern above) is taken as the key, and the remainder of the line (after the whitespace) as its value (singleton above). The tag's name is not verified in any way, and the value is not parsed. This is the simplest way to use attributes.
To gain more control over attribute validation, run the compiler in
"object" mode. The attribute compiler then assumes that each tag refers
to an attribute class that it will attempt to find an instantiate. The
tag's name up to the first whitespace is taken as the name of the class.
The name can be fully qualified, or it can rely on the compilation unit's
declared package or imports to find the correct class, taking advantage of the
usual Java type resolution rules. The name can also
refer to a nested class in the usual way, by separating nested class names
with dots (e.g. Server.TransactionAttribute
). Furthermore,
if the name does not end with Attribute
, this word will be
appended before looking for a class; however, if this fails, the original name
will be tried too.
Attribute classes should normally be serializable, so that attribute instances can be saved directly in the compiled attribute files. If an attribute is not serializable, however, it will be stored as its class name and string parameters, and recreated at runtime as necessary.
Once the attribute class has been found, the compiler will attempt to instantiate it by using the tag's parameters. Two kinds of parameters are recognized: positional and named. All positional parameters must precede all named parameters, though both sets are optional. Parameters are separated by whitespace. The positional parameters will be used to locate a constructor for the attribute class that takes the same number of String parameters as there are positional parameters. The named parameters will be matched to properties of the attribute class, and each property's set method will be invoked with the given string as argument. For example:
@Tx write pessimistic isolate=full
This attribute will be instantiated as follows:
TxAttribute attr = new TxAttribute("write", "pessimistic"); attr.setIsolate("full");
Properties will be located using standard JavaBeans introspection. The order of named parameters is usually not important, though this depends on whether the properties mentioned are all independent of each other. In any case, the properties will be set in the order listed in the tag. Either the constructor or any of the property setters can throw exceptions (checked or unchecked) to indicate that the parameters are unacceptable; these will be reported as tag syntax errors by the compiler.
If you wish to include whitespace or other special characters in your parameters, simply enclose them in double quotes. Inside the double quotes, spaces will be considered as part of the parameter. (This also works on named parameter values.) To include a double quote in the string, use \", use \\ for a backslash, and \n and \t to insert a newline or tab respectively.
Finally, you can also run the compiler in "mixed" mode, where any tag that is not recognized as an attribute class name will be processed as a simple "string" attribute.
This section concentrates on the command-line compiler. The Ant task works similarly, but all parameters are passed through the build definition file instead. Make sure you have attrib-dev.jar and the included qdox-tiny.jar on your classpath. Also present on your classpath should be all the classes normally needed to compile the code you'll be compiling attributes for, and the attribute classes themselves, if any.
To compile all attributes in your project, change to the root of your source hierarchy (i.e. the package root) and run the compiler:
java com.ideanest.attributes.compiler.Compiler
The resulting attribute files will be placed next to the source files. This works best if you normally generate your class files in the same place as your source files. If you use separate directories, say "source" for source files and "build" for build files, use this command line from your project root directory instead:
java com.ideanest.attributes.compiler.Compiler -src source -dst build
This will compile the attributes in all source files in the directories below "source" and place the attribute files into matching subdirectories of "build". If you have multiple source and build directories, you can list them all (type all of this on one line):
java com.ideanest.attributes.compiler.Compiler -src source;tests -dst build;testbuild
All source files in the "source" and "tests" directories will be compiled, and the attribute files will be placed wherever the matching class files are found in "build" and "testbuild". (If a source file doesn't have a matching class file, the attribute file will be placed into the first destination directory listed, "build" in this case.) The source and destination directories are separated by the same character used to separate classpath entries (normally ";" on Windows and ":" on Unix).
Finally, if you only want to compile attributes for classes in the
com.example.foo
package and subpackages, even though its source files
are spread across the source and tests directories, do this:
java com.ideanest.attributes.compiler.Compiler -src source;tests -dst build;testbuild com/example/foo
You can list any number of directories (and Java source code files) in this manner, and only those files and directory hierarchies will be considered by the attribute compiler.
A full list of compiler options follows:
-help | Print usage and exit. | ||||||
-src <source paths> | Specify the list of package root directories that contain source code. Separate directories with the platform's path separator character (like classpath). If not specified, use the current directory as the default source root. | ||||||
-dst <destination paths> | Specify the list of package root directories that will receive compiled attribute files. Separate directories with the platform's path separator character (like classpath). Each attribute file will go into the same directory that contains the matching class file, or the first directory listed if none is found. If not specified, use the source paths as the default destination paths. | ||||||
-mode <string|object|mixed> | Set the compiler parsing mode:
The default mode is "mixed". |
||||||
-ignore <tags to ignore> | Set the list of tags to ignore when processing. The tags must not include the @ sign, and must be comma-separated. If not specified, default to ignoring all standard Javadoc tags, so that they won't be processed as attributes. If specified, though, standard Javadoc tags are not automatically ignored and must be listed if so desired. | ||||||
-force | Force all attribute files to be regenerated, regardless of file timestamps. | ||||||
-nocleanup | Do not remove attribute files with no matching source file. You must use this option if you have non-public classes in files whose names don't match the class name. | ||||||
-verbose | Print extra information on the compilation process. |
At runtime, you need to have the attrib-rt.jar (or attrib-dev.jar, which is a superset) on your classpath. To access attributes, use the
com.ideanest.attributes.Attributes
singleton. Some typical uses:Get the value of a simple string attribute for a class:
Attributes.getInstance().get(MyClass.class).get("mytag");
To check whether a simple string attribute with a given tag was defined for a class:
Attributes.getInstance().get(MyClass.class).has("mytag");Get the value of an object attribute for a field:
Attributes.getInstance()
.get(MyClass.class.getField("myfield"))
.get(TxAttribute.class);Get an iterator for all the attributes of a method:
Attributes.getInstance()
.get(MyClass.class.getMethod("toString", null))
.iterator();Get an iterator for all values of string attributes with a given tag for a class:
Attributes.getInstance()
.get(MyClass.class)
.iterator("my_multiple_tag");Many other accessors are available; check the Javadocs for details. A few things to keep in mind:
![]() | The order of all attributes is preserved for each element, and all
iterators and toArray() will respect it. Hence, the ordering of
tags can be meaningful if you'd like. |
![]() | String and object attributes never overlap. Each tag is parsed as
either one or the other, never both. Thus, you will not find object
attributes by querying for the tags used to define them. (However, you
will find string attributes as object attributes of class
SimpleAttribute .) |
![]() | You can access attribute bundles using coded strings rather than reflected element references. This can be useful if the element you wish to get the attributes for is not visible from the accessing code, or you need to access the attributes before the class containing the element is loaded. |
![]() | You can add (and sometimes remove) attributes at runtime, but these changes will not persisted back to disk. |
When putting your application in a JAR, make sure that all the attribute files generated by the compiler stay with their matching class files. That's it -- you don't need to do anything more!
As an optional step, you can compact the attribute files inside the JAR into one big attribute file. This should decrease the overall loading time of attributes, and may reduce the overall size as well. To compact the attribute files, run the following command line passing it any number of JAR filenames as arguments. They will be compacted in place.
java com.ideanest.attributes.dev.JarCompacter myfilename.jar