| |
Javolution | License: | | URL: | http://javolution.org/ | Description: | Javolution real-time goals are simple: To make your application faster and more time predictable! |
Package Name | Comment | j2me.io | | j2me.lang | | j2me.nio | | j2me.text | | j2me.util | | j2me.util.concurrent | | j2me.util.logging | | j2me.util.zip | | j2mex.realtime | | javolution | | javolution.context |
Provides real-time {@link javolution.context.Context} to facilitate
separation of concerns and achieve higher level of performance and
code predictability.
Separation of concerns is an important design principle greatly misenderstood.
Most developers think it is limited to modularity and encapsulation or it
requires special programming tools (e.g. Aspect programming).
Separation of concerns is very powerful and easier than it looks.
Basically, it could be summarized as the "pass the buck principle".
If you don't know what to do with some information, just give it to someone
else who might know.
A frequent example is the catching of exceptions too early (with some logging processing)
instead of throwing a checked exception. Unfortunately, they are still plenty of cases
where the separation of concerns is not as good as it could be. For example logging!
Using the standard logging, the code has to know which logger to log to? Why?
Separation of concerns can be adressed through "Aspect Programming",
but there is a rather simpler solution "Context Programming"!
It does not require any particular tool, it basically says that every threads
has a context which can be customized by someone else (the one who knows what to do).
Then, your code looks a lot cleaner and is way more flexible as you don't have
to worry about logging, security, performance etc. in your low level methods.
For example:[code]
void myMethod() {
...
LogContext.info("Don't know where this is going to be logged to");
...
}[/code]
Used properly Javolution's {@link javolution.context.Context contexts}
greatly facilitate the separation of concerns. Contexts are
complemented by others classes such as for example the
{@link javolution.lang.Configurable Configurable} class to reduce
dependency between configuration and application code.
This package provides few predefined contexts:
- {@link javolution.context.LocalContext LocalContext} - To define locally
scoped environment settings.
- {@link javolution.context.ConcurrentContext ConcurrentContext} - To take advantage of concurrent
algorithms on multi-processors systems.
- {@link javolution.context.AllocatorContext AllocatorContext} - To control
object allocation, e.g. {@link javolution.context.StackContext StackContext}
to allocate on the stack (or RTSJ ScopedMemory).
- {@link javolution.context.LogContext LogContext} - For thread-based or object-based logging
capability, e.g. {@link javolution.util.StandardLog StandardLog} to leverage standard
logging capabilities.
Note:
java.util.logging provides class-based
logging (based upon class hierarchy).
- {@link javolution.context.PersistentContext PersistentContext} - To achieve persistency across
multiple program execution.
- {@link javolution.context.SecurityContext SecurityContext} - To address application-level security
concerns.
- {@link javolution.testing.TestContext TestContext} - To address varied aspect of testing such as performance and regression.
- I am writing an application using third party libraries.
I cannot avoid GC unless I get the source and patch it to Javolution.
Can I still make my application real-time using {@link javolution.context.StackContext StackContext}?
You cannot get determinism using "any" library (including Java standard library)
regardless of the garbage collector issue. Array resizing, lazy initialization, map rehashing (...)
would all introduce unexpected delays (this is why Javolution comes with its own
{@link javolution.lang.Realtime real-time} collections implementation).
Still, you may use incremental/real-time collectors (if few milliseconds delays are acceptable).
These collectors work even faster if you limit the amount of garbage produced onto the heap
through {@link javolution.context.StackContext stack allocations}.
- Can you explain a little how objects can be "stack" allocated?
It all depends upon the StackContext {@link javolution.context.StackContext#DEFAULT default} implementation.
The default implementation use thread-local queues (no synchronization required);
but if you run on a RTSJ virtual machine
entering a {@link javolution.context.StackContext StackContext} could mean using ScopedMemory .
- As a rule, I am skeptical of classes that pool small objects.
At one time (5 years ago) it was a big win. Over time, the advantage has
diminished as garbage collectors improve. Object pools can make it much more
difficult for the garbage collector to do its job efficiently, and can have
adverse effects on footprint. (Joshua Bloch)
Stack allocation is different from object pooling, it is a simple and transparent
way to make your methods "clean" (no garbage generated), it has also the side effect
of making your methods faster and more time-predictable.
If all your methods are "clean" then your whole
application is "clean", faster and more time-predictable (aka real-time).
In practice very few methods need to enter a {@link javolution.context.StackContext StackContext},
only the one generating a significant number of temporary objects (these methods are made "cleaner"
and faster through stack allocation). For example:[code]
public final class DenseVector> extends Vector {
...
public F times(Vector that) {
final int n = this.getDimension();
if (that.getDimension() != n) throw new DimensionException();
StackContext.enter();
try { // Reduces memory allocation / garbage collection.
F sum = this.get(0).times(that.get(0));
for (int i = 1; i < n; i++) {
sum = sum.plus(this.get(i).times(that.get(i)));
}
return StackContext.outerCopy(sum); // Stack object exported through copy.
} finally {
StackContext.exit(); // Resets stack.
}
}
...
}[/code]
| javolution.io |
Provides utility classes for input and output (such as
{@link javolution.io.Struct Struct} and {@link javolution.io.Union Union}
for direct interoperability with C/C++).
| javolution.lang |
Provides fundamental classes and interfaces; some of which are either missing from
the java.lang package or are not available for all platforms (including J2ME CLDC).
| javolution.testing |
Provides classes and interfaces to facilitate all aspects of testing including
unit tests, performance, regression, etc.
Too often unit tests focus on one aspect: "Validation". But although a code
modification might not break your application; it may very well impact the
performance significantly (for the better or the worst).
External elements (JVM, O/S, memory available, runtime options) are also likely to affect
performance. It is therefore important to not only be able to measure the
performance but also to detect automatically (regression tests) when any change
you make in your code or runtime environment breaks your timing assumptions.
This test framework addresses not only the validation aspect of testing but
performance and regression as well.
In a normal situation, the developer creates a
{@link javolution.testing.TestSuite TestSuite} which is basically
a collection of {@link javolution.testing.TestCase TestCase} logically grouped
together.
(Note: You will find examples of test suites in the javolution.* source directory).
Then by running within an appropriate {@link javolution.testing.TestContext TestContext}, the developer
can focus on any particular aspect of interest (behavior, performance, memory usage, ...)
For example:[code]
// Default tests execution, simple validation and logging of the results.
new MyTestSuite().run();
// Specialized context measuring execution time (default average time, with minimum time in parenthesis).
TimeContext.enter();
try {
new MyTestSuite().run();
} finally {
TimeContext.exit();
}
// Regression tests (no output, AssertionException raised if any test fails).
TestContext.enter(TestContext.REGRESSION); // Or TimeContext.REGRESSION for performance regression test.
try {
new MyTestSuite().run();
} finally {
TestContext.exit();
}
[/code]
Logging/tests contexts do not have to output the results in a text form.
Implementations may store results in databases, spreadsheets or show them graphically.
For example:[code]
// Logs output to console.
LogContext.enter(LogContext.CONSOLE);
try {
new MyTestSuite().run();
} finally {
LogContext.exit();
}[/code]
| javolution.text |
Provides classes and interfaces to handle text.
- Is parsing/formatting of floating-points numbers (e.g.
double )
equivalent to standard String/Double methods?
With Javolution 4.1, double formatting/parsing is lossless
and functionally the same as with the standard library. Parsing a character
sequence will always result in the same number whether it is performed with
{@link javolution.text.TypeFormat TypeFormat} or using Double.parseDouble(String)) .
When formatting a double number, the number of digits output
is adjustable. The default (if the number of digits is unspecified) is 17
or 16 when the the 16 digits representation can be parsed back to
the same double (mimic the standard library formatting).
Javolution parsing/formatting do not generate garbage and has no adverse
effect on GC. Better, it does not force the user to create intermediate String
objects, any CharSequence/Appendable can be used! Serial parsing is also supported
(cursor parameter).
- I'm accumulating a large string, and all I want to do is
append to the end of the current string, which is the better class to use,
Text or TextBuilder? Bearing in mind that speed is important, but I also want
to conserve memory.
It all depends of the size of the text to append (the actual size of the
document being appended has almost no impact in both cases).
If you append one character at a time or a small text then
{@link javolution.text.TextBuilder#append(Object) TextBuilder.append(Object)}
is faster (the cost of copying the characters to the internal buffer is
then negligeable and TextBuilder never resizes its internal arrays).
If you append larger character sequences (the threshold might be around 20
characters) then {@link javolution.text.Text#concat(Text) Text.concat(Text)}
is more efficient (it avoid character copies, but creates small nodes objects
instead).
- In our project's use of strings, there are a lot of
instances of directory path names, such as
"/proj/lodecase/src/com/lodecase/util/foo.java" , and
"/proj/lodecase/src/com/lodecase/util/bar.java" .
Can the 'Text' class save us memory when strings
have common prefixes?
It depends how you build your text. For example in following code:[code]
Text directoryName = Text.valueOf("/proj/lodecase/src/com/lodecase/util/");
Text fooFileName = directoryName.plus("foo.java");
Text barFileName = directoryName.plus("bar.java");[/code]
The prefix (directoryName)is shared between fooFileName and barFileName .
Text is a binary tree of blocks of characters. In the example,
above, fooFileName is a node with directoryName for
head and "foo.java" for tail. The tree is maintained balanced automatically
through tree rotations.
| javolution.util |
Provides high-performance collection classes and miscellaneous utilities; although
this package provides very few collection classes, they are substitutes for
most of java.util.* classes (for example, java.util.IdentityHashMap would be
a {@link javolution.util.FastMap FastMap} with an {@link javolution.util.FastComparator#IDENTITY
identity} key comparator).
Javolution collections are compliant with standard collections
(generic when built with the ant target 1.5 ) and they can safely be used
with RTSJ virtual machines (e.g. if the capacity of
a collection increases, the extension part is allocated from the same memory
area as the collection itself).
They support direct iterations with the following advantages:
- Faster than iterators, see benchmark.
- No object creation not even the iterator object itself. For example, visiting a tree structure using
iterators creates as many iterators as they are nodes in the tree:[code]
public static void visit(Collection node) {
for (Collection i : node) { // Creates iterator.
visit(i);
}
}[/code]
Not so with direct iterations:[code]
public static void visit(FastCollection node) {
for (FastCollection.Record r = node.head(), end = node.tail(); (r = r.getNext()) != end;) {
visit(node.valueOf(r));
}
}[/code]
- Used to implement most of {@link javolution.util.FastCollection FastCollection} base class methods
(including {@link javolution.util.FastCollection#iterator iterator()}).
- Support forward/backward iterations from the start (head) or from the end (tail)
- Thread-safe as long as objects are not inserted/removed during iterations. Objects can safely be
append/prepend by the current thread or other threads.
(Note: {@link javolution.util.FastMap#setShared Shared FastMap} are always thread-safe even when entries are removed).
- Fully integrated with the JDK1.5+ generic framework (strong typing) and still compatible
with other platforms (J2ME, 1.4, GCJ).
Here are few examples of direct iterations:[code]
FastList list;
for (FastList.Node n = list.head(), end = list.tail(); (n = n.getNext()) != end;) {
String value = n.getValue(); // No typecast necessary.
}
...
FastMap map;
for (FastMap.Entry e = map.head(), end = map.tail(); (e = e.getNext()) != end;) {
String key = e.getKey(); // No typecast necessary.
Thread value = e.getValue(); // No typecast necessary.
}[/code]
Users may provide a read-only view of any {@link javolution.util.FastCollection FastCollection}
(or {@link javolution.util.FastMap FastMap}) instance using the
{@link javolution.util.FastCollection#unmodifiable() FastCollection.unmodifiable()}
(or {@link javolution.util.FastMap#unmodifiable FastMap.unmodifiable()}) method.
For example:[code]
public class Polynomial {
private final FastSet _terms = new FastSet();
// Read-only view (also thread-safe as terms are not "deleted").
public Set getTerms() {
return _terms.unmodifiable();
}
}[/code]
Collection/maps of primitive types can be created using the
{@link javolution.util.Index Index} class. It avoids the overhead
of wrapping primitives types (for reasonably small int values).
For example:[code]
public class SparseVector {
FastMap _elements = new FastMap();
...
}[/code]
Although all collections capacity increases smoothly (no resizing/copy or rehashing ever performed),
it is nevertheless possible to specify an initial capacity; in which case, all necessary storage
is allocated at creation. For RTSJ VMs, all
collections/maps can reside in ImmortalMemory (e.g. static )
and be used by all threads (including NoHeapRealtimeThread ) without resulting into memory leaks
or illegal access errors. For example:[code]
public class XmlFormat {
// RTSJ Unsafe! Memory leaks (when entries removed) or IllegalAssignmentError (when new entries while in ScopedArea).
static HashMap ClassToFormat = HashMap();
// RTSJ Safe! Removed entries are internally recycled, new entries are in ImmortalMemory.
static FastMap ClassToFormat = FastMap();
}[/code]
For more details, please read Javolution-Collection.pdf .
Temporary collection classes can be recycled (e.g. throw-away collections) to avoid the creation
cost. For example:[code]
static void removeDuplicate(List persons) {
FastSet tmp = FastSet.newInstance(); // Possibly recycled instance.
tmp.addAll(persons);
persons.clear();
persons.addAll(tmp);
FastSet.recycle(tmp); // Recycles the temporary instance.
}[/code]
Here is a summary of the collection classes with their defining characteristics:
Javolution Collections Classes
|
Ordering |
Duplication Allowed |
Custom Comparators |
Record Type |
Miscellaneous |
{@link javolution.util.FastTable FastTable} |
Insertion Order |
Yes |
{@link javolution.util.FastTable#setValueComparator setValueComparator(FastComparator)} |
{@link javolution.util.Index Index} |
Thread-safe random access collection
No array resize/copy ever performed |
{@link javolution.util.FastList FastList} |
Insertion Order |
Yes |
{@link javolution.util.FastList#setValueComparator setValueComparator(FastComparator)} |
{@link javolution.util.FastList.Node Node} |
Recycle their own nodes (no adverse effect on GC) |
{@link javolution.util.FastSet FastSet} |
Insertion Order |
No |
{@link javolution.util.FastSet#setValueComparator setValueComparator(FastComparator)} |
{@link javolution.util.FastCollection.Record Record} |
Based on {@link javolution.util.FastSet FastMap} (same characteristics) |
FastTree |
Comparator |
No |
setValueComparator(FastComparator) |
TreeNode |
(not implemented) |
{@link javolution.util.FastMap FastMap} |
Insertion Order |
Key: No Value: Yes |
{@link javolution.util.FastMap#setKeyComparator setKeyComparator(FastComparator)}
{@link javolution.util.FastMap#setValueComparator setValueComparator(FastComparator)} |
{@link javolution.util.FastMap.Entry Entry} |
Thread-safe when marked as {@link javolution.util.FastMap#setShared shared}
No rehash/resize ever performed
Recycle their own entries (no adverse effect on GC) |
- ArrayList may throw ConcurrentModificationException,
but Javolution FastTable does not, why?
FastTable (or any Javolution collection/map) do support concurrent modifications
as long as these are not insertions at an arbitrary position or deletions (Note: Shared FastMap
does support concurrent deletions).
In other words you can safely iterate (using iterators or not) through a FastList, FastMap
(entries, keys values), FastTable, etc. while new elements/entries are being added
(by you or another thread). You can also export a {@link javolution.util.FastCollection#unmodifiable() read-only}
view over your collection and still add more elements to it.
Disallowing concurrent modifications (standard java util) has proven to be a performance
killer for many (forcing users to work with copies of their whole collections). Furthermore the additional checks required
directly impact performance (e.g. ArrayList iterations about 3x slower than FastTable iterations).
- Do you have a test case showing any scenario of concurrent modification where
ArrayList "fails" and FastTable doesn't?
Let's say that you have a collection of "Units", and you want to provide users
with a read-only view of these units. The following code will fail miserably:[code]
public class Unit {
static ArrayList INSTANCES = new ArrayList();
public static List getInstances() {
return Collections.unmodifiableList(INSTANCES);
}
}[/code]
Why? Because, it the user iterates on the read-only list of units while a new unit is added
to the collection (by another thread) a ConcurrentModificationException is
automatically raised. In other words, it is almost impossible to provide a "read-only" view
of non-fixed size collections with the current java.util classes (e.g. you will have to replace
the whole collection each time a new unit is added).
Now with FastTable the following is completely safe even when new units are added:[code]
public class Unit {
static FastTable INSTANCES = new FastTable();
public static List getInstances() {
return INSTANCES.unmodifiable();
}
}[/code]
- Do checks for concurrent modifications make your code safer?
Not really. The current checks for concurrent modifications do not "guarantee" that concurrent
modifications will not occur! You can imagine two threads one updating a collection
and the other one iterating the collection. As long as the update is not performed
while the other thread is iterating, everything is fine (no ConcurrentModificationException)!
But, if for a reason or another the timing changes (e.g. in the user environment) and
iterations are performed at the wrong time then your application crashes...
Not a good thing and very high probability for this to happen!
- Are {@link javolution.util.FastMap#setShared shared maps} valid substitutes for
ConcurrentHashMap ?
Unlike ConcurrentHashMap access to a
shared FastMap never blocks. Retrieval reflects the map state not older than the last
time the accessing threads have been synchronized* (for multi-processors
systems synchronizing ensures that the CPU internal cache is not stale).
In practice, it means that most well-written concurrent programs should
be able to use shared FastMap in place of ConcurrentHashMap as
threads are already synchronized to ensure proper behavior.
* It is important for both threads to synchronize on the same monitor
in order to set up the happens-before relationship properly. It is not the case
that everything visible to thread A when it synchronizes on object X becomes visible
to thread B after it synchronizes on object Y. The release and acquire have to
"match" (i.e., be performed on the same monitor) to have the right semantics.
Otherwise, the code has a data race.
- Are all Javolution collection thread-safe?
Collections/Maps are thread-safe with regard to access (no need to
synchronize reading even if the collection is modified concurrently).
But the modifications themselves require either the collection/map to be
marked shared or synchronization to be used.
- What is the overhead in term of performance when
FastMap.setShared
is set to true ?
Marking the map shared avoid synchronizing when possible (e.g. put when
entry already exists or remove when entry does not exist), if a new entry
is created and added, synchronization is performed internally. In all
cases there is no impact on reading (never synchronized).
| javolution.xml |
Provides support for the encoding of objects, and the objects reachable from them,
into XML ; and the complementary reconstruction of the
object graph from XML .
Key Advantages:
- Real-time characteristics with no adverse effect on memory footprint or
garbage collection (e.g. it can be used for time critical communications).
{@link javolution.xml.XMLFormat XMLFormat} is basically a "smart"
wrapper around our real-time StAX-like
{@link javolution.xml.stream.XMLStreamReader XMLStreamReader} and
{@link javolution.xml.stream.XMLStreamWriter XMLStreamWriter}.
- Works directly with your existing Java classes, no need to create new classes
or customize your implementation in any way.
- The XML representation can be high level and impervious to obfuscation
or changes to your implementation.
- Performance on a par or better than default JavaTM Serialization/Deserialization
(See bindmark for performance comparison).
- Runs on any platform including J2ME CLDC 1.0 It does not require reflection or
or any interface (e.g.
Serializable ) to be implemented.
- The XML mapping can be defined for a top class (or interface) and is automatically
inherited by all sub-classes (or all implementing classes).
- Supports object references (to avoid expanding objects already formatted).
The default XML mapping for a class and its sub-classes is typically defined using
a static final {@link javolution.xml.XMLFormat XMLFormat} instance.
For example:[code]
public abstract class Graphic implements XMLSerializable {
private boolean _isVisible;
private Paint _paint; // null if none.
private Stroke _stroke; // null if none.
private Transform _transform; // null if none.
// Default XML format with name associations (members identified by an unique name).
// See XMLFormat for examples of positional associations.
protected static final XMLFormat XML = new XMLFormat(Graphic.class) {
public void write(Graphic g, OutputElement xml) throws XMLStreamException {
xml.setAttribute("isVisible", g._isVisible);
xml.add(g._paint, "Paint");
xml.add(g._stroke, "Stroke");
xml.add(g._transform, "Transform");
}
public void read(InputElement xml, Graphic g) throws XMLStreamException {
g._isVisible = xml.getAttribute("isVisible", true);
g._paint = xml.get("Paint");
g._stroke = xml.get("Stroke");
g._transform = xml.get("Transform");
}
};
}[/code]
Sub-classes may override the inherited XML format:[code]
public class Area extends Graphic {
private Shape _geometry;
// Adds geometry to format.
protected static final XMLFormat XML = new XMLFormat(Area.class) {
public void write(Area area, OutputElement xml) throws XMLStreamException {
Graphic.XML.write(area, xml); // Calls parent write.
xml.add(area._geometry, "Geometry");
}
public void read(InputElement xml, Area area) throws XMLStreamException {
Graphic.XML.read(xml, area); // Calls parent read.
area._geometry = xml.get("Geometry");
}
};
}[/code]
The following writes a graphic area to a file, then reads it:[code]
// Creates some useful aliases for class names.
XMLBinding binding = new XMLBinding();
binding.setAlias(Color.class, "Color");
binding.setAlias(Polygon.class, "Polygon");
binding.setClassAttribute("type"); // Use "type" instead of "class" for class attribute.
// Writes the area to a file.
XMLObjectWriter writer = XMLObjectWriter.newInstance(new FileOutputStream("C:/area.xml"));
writer.setBinding(binding); // Optional.
writer.setIndentation("\t"); // Optional (use tabulation for indentation).
writer.write(area, "Area", Area.class);
writer.close();
// Reads the area back
XMLObjectReader reader = XMLObjectReader.newInstance(new FileInputStream("C:/area.xml"));
reader.setBinding(binding);
Area a = reader.read("Area", Area.class);
reader.close();
[/code]
Here is an example of valid XML representation for an area:[code]
[/code]
The following table illustrates the variety of XML representations supported
(Foo class with a single String member named text):
XML FORMAT |
XML DATA |
[code]XMLFormat XML = new XMLFormat(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.setAttribute("text", foo.text);
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.getAttribute("text", "");
}
};[/code] |
<!-- Member as attribute -->
<Foo text="This is a text"/>
|
[code]XMLFormat XML = new XMLFormat(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.add(foo.text);
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.getNext();
}
};[/code] |
<!-- Member as anonymous nested element -->
<Foo>
<java.lang.String value="This is a text"/>
</Foo>
|
[code]XMLFormat XML = new XMLFormat(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.addText(foo.text);
// or xml.getStreamWriter().writeCDATA(foo.text) to use CDATA block.
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.getText().toString(); // Content of a text-only element.
}
};[/code] |
<!-- Member as Character Data -->
<Foo>This is a text</Foo>
|
[code]XMLFormat XML = new XMLFormat(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.add(foo.text, "Text");
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.get("Text");
}
};[/code] |
<!-- Member as named element of unknown type -->
<Foo>
<Text class="java.lang.String" value="This is a text"/>
</Foo>
|
[code]XMLFormat XML = new XMLFormat(Foo.class) {
public void write(Foo foo, OutputElement xml) throws XMLStreamException {
xml.add(foo.text, "Text", String.class);
}
public void read(InputElement xml, Foo foo) throws XMLStreamException {
foo.text = xml.get("Text", String.class);
}
};[/code] |
<!-- Member as named element of actual type known -->
<Foo>
<Text value="This is a text"/>
</Foo>
|
The {@link javolution.xml.XMLFormat XMLFormat} does not have to use the class
public no-arg constructor, instances can be created using factory methods,
private constructors (with constructor parameters set from the XML element) or even retrieved from a collection
(if the object is shared or unique). For example:[code]
public final class Point implements XMLSerializable {
// Default XMLFormat can be private as the class cannot be extended.
static final XMLFormat XML = new XMLFormat(Point.class) {
public boolean isReferencable() {
return false; // Always manipulates by value.
}
public Point newInstance(Class cls, InputElement xml) throws XMLStreamException {
return Point.valueOf(xml.getAttribute("x", 0), xml.getAttribute("y", 0));
}
public void write(Point point, OutputElement xml) throws XMLStreamException {
xml.setAttribute("x", point._x);
xml.setAttribute("y", point._y);
}
public void read(InputElement xml, Point point) throws XMLStreamException {
// Do nothing immutable.
}
};
private int _x;
private int _y;
private Point() {}; // No-arg constructor not visible.
public static Point valueOf(int x, int y) { ... }
}[/code]
Document cross-references are supported, including circular references.
Let's take for example:[code]
public class Polygon implements Shape, XMLSerializable {
private Point[] _vertices;
static final XMLFormat XML = new XMLFormat(Polygon.class) {
public void write(Polygon polygon, OutputElement xml) throws XMLStreamException {
xml.setAttibutes("count", _vertices.length);
for (int i=0; i < _vertices.length; i++) {
xml.add(_vertices[i], "Vertex", Point.class);
}
}
public void read(InputElement xml, Polygon polygon) throws XMLStreamException {
int count = xml.getAttributes("count", 0);
polygon._vertices = new Point[count];
for (int i=0; i < count; i++) {
_vertices[i] = xml.get("Vertex", Point.class);
}
}
};
}
Polygon[] polygons = new Polygon[] {p1, p2, p1};
...
TextBuilder xml = TextBuilder.newInstance();
AppendableWriter out = new AppendableWriter().setOutput(xml)
XMLObjectWriter writer = XMLObjectWriter.newInstance(out);
writer.setXMLReferenceResolver(new XMLReferenceResolver()); // Enables cross-references.
writer.write(polygons, "Polygons", Polygon[].class);
writer.close();
System.out.println(xml);
[/code]
Prints the following (noticed that the first polygon and last one are being shared).[code]
[/code]
ALGORITHMS:
Our {@link javolution.xml.XMLObjectReader XMLObjectReader}/{@link javolution.xml.XMLObjectWriter XMLObjectWriter}
are in fact simple wrappers around our Javolution high-performance StAX-like
{@link javolution.xml.stream.XMLStreamReader XMLStreamReader} and
{@link javolution.xml.stream.XMLStreamWriter XMLStreamWriter} classes.
The logic of these wrappers is described below:
Marshalling:
Input: object, referenceResolver, binding
Output: outputElement
1. class = object.getClass()
2. outputElement.getStreamWriter().writeStartElement(name/uri)
The new element name/uri is:
a - Specified by caller, e.g. add(object, name, uri, class)
If the class is not specified e.g. add(object, name, uri) a class attribute is written.
b - binding.getLocalName(class)/binding.getURI(class), e.g add(object)
3. isReference = referenceResolver.writeReference(object, outputElement)
4. if (!isReference) binding.getFormat(class).write(object, outputElement)
5. outputElement.getStreamWriter().writeEndElement()
6. end
Unmarshalling:
Input: inputElement, referenceResolver, binding
Output: object
1. object = referenceResolver.readReference(inputElement)
2. if (object != null) Goto 8 // Found reference
3. class is either:
a - Specified by caller, e.g. get("name", class)
b - binding.getClass(class attribute value)
c - binding.getClass(element name, element uri)
4. format = binding.getFormat(class)
5. object = format.newInstance(class, inputElement)
6. referenceResolver.createReference(object, inputElement) // Done before parsing to support circular references.
7. format.read(inputElement, object)
8. inputElement.getStreamReader().nextTag()
9. end
| javolution.xml.sax |
Provides {@link javolution.xml.sax.SAX2ReaderImpl SAX2} and
{@link javolution.xml.sax.XMLReaderImpl SAX2-Like} parsers; the later
being several times faster than conventional SAX2 parsers (by avoiding
String allocations while parsing).
| javolution.xml.stream |
Provides StAX-like XML readers/writers which do not require object
creation (such as String) and are consequently faster and more time predictable
than standard StAX classes.
The main difference with "javax.xml.stream.*" classes is the use
of CharSequence instead of String . Since
String is a CharSequence (JDK 1.4+), most
existing StAX code requires very little modification to be used with these
new classes.
For more information about the usage of this package please read the
documentation for the {@link javolution.xml.stream.XMLStreamReader} and
{@link javolution.xml.stream.XMLStreamWriter} interfaces.
For more information about StAX (Streaming API for XML) in general see
Wikipedia: StAX
| javolution.xml.ws |
Provides classes and interfaces to create and handle web services.
At this stage, only a simple base web service client is provided.
Future releases may include more advanced clients (e.g. parsing WSDL files),
classes to easily create web service server, etc.
| org.xml.sax |
This package provides the core SAX APIs.
Some SAX1 APIs are deprecated to encourage integration of
namespace-awareness into designs of new applications
and into maintenance of existing infrastructure.
See http://www.saxproject.org
for more information about SAX.
SAX2 Standard Feature Flags
One of the essential characteristics of SAX2 is that it added
feature flags which can be used to examine and perhaps modify
parser modes, in particular modes such as validation.
Since features are identified by (absolute) URIs, anyone
can define such features.
Currently defined standard feature URIs have the prefix
http://xml.org/sax/features/ before an identifier such as
validation . Turn features on or off using
setFeature. Those standard identifiers are:
Feature ID |
Access |
Default |
Description |
external-general-entities |
read/write |
unspecified |
Reports whether this parser processes external
general entities; always true if validating.
|
external-parameter-entities |
read/write |
unspecified |
Reports whether this parser processes external
parameter entities; always true if validating.
|
is-standalone |
(parsing) read-only, (not parsing) none |
not applicable |
May be examined only during a parse, after the
startDocument() callback has been completed; read-only.
The value is true if the document specified standalone="yes" in
its XML declaration, and otherwise is false.
|
lexical-handler/parameter-entities |
read/write |
unspecified |
A value of "true" indicates that the LexicalHandler will report
the beginning and end of parameter entities.
|
namespaces |
read/write |
true |
A value of "true" indicates namespace URIs and unprefixed local names
for element and attribute names will be available.
|
namespace-prefixes |
read/write |
false |
A value of "true" indicates that XML qualified names (with prefixes) and
attributes (including xmlns* attributes) will be available.
|
resolve-dtd-uris |
read/write |
true |
A value of "true" indicates that system IDs in declarations will
be absolutized (relative to their base URIs) before reporting.
(That is the default behavior for all SAX2 XML parsers.)
A value of "false" indicates those IDs will not be absolutized;
parsers will provide the base URI from
Locator.getSystemId().
This applies to system IDs passed in
- DTDHandler.notationDecl(),
- DTDHandler.unparsedEntityDecl(), and
- DeclHandler.externalEntityDecl().
It does not apply to EntityResolver.resolveEntity(),
which is not used to report declarations, or to
LexicalHandler.startDTD(), which already provides
the non-absolutized URI.
|
string-interning |
read/write |
unspecified |
Has a value of "true" if all XML names (for elements, prefixes,
attributes, entities, notations, and local names),
as well as Namespace URIs, will have been interned
using j2me.lang.String.intern. This supports fast
testing of equality/inequality against string constants,
rather than forcing slower calls to String.equals().
|
unicode-normalization-checking |
read/write |
false |
Controls whether the parser reports Unicode normalization
errors as described in section 2.13 and Appendix B of the
XML 1.1 Recommendation. If true, Unicode normalization
errors are reported using the ErrorHandler.error() callback.
Such errors are not fatal in themselves (though, obviously,
other Unicode-related encoding errors may be).
|
use-attributes2 |
read-only |
not applicable |
Returns "true" if the Attributes objects passed by
this parser in ContentHandler.startElement()
implement the org.xml.sax.ext.Attributes2 interface.
That interface exposes additional DTD-related information,
such as whether the attribute was specified in the
source text rather than defaulted.
|
use-locator2 |
read-only |
not applicable |
Returns "true" if the Locator objects passed by
this parser in ContentHandler.setDocumentLocator()
implement the org.xml.sax.ext.Locator2 interface.
That interface exposes additional entity information,
such as the character encoding and XML version used.
|
use-entity-resolver2 |
read/write |
true |
Returns "true" if, when setEntityResolver is given
an object implementing the org.xml.sax.ext.EntityResolver2 interface,
those new methods will be used.
Returns "false" to indicate that those methods will not be used.
|
validation |
read/write |
unspecified |
Controls whether the parser is reporting all validity
errors; if true, all external entities will be read.
|
xmlns-uris |
read/write |
false |
Controls whether, when the namespace-prefixes feature
is set, the parser treats namespace declaration attributes as
being in the http://www.w3.org/2000/xmlns/ namespace.
By default, SAX2 conforms to the original "Namespaces in XML"
Recommendation, which explicitly states that such attributes are
not in any namespace.
Setting this optional flag to "true" makes the SAX2 events conform to
a later backwards-incompatible revision of that recommendation,
placing those attributes in a namespace.
|
xml-1.1 |
read-only |
not applicable |
Returns "true" if the parser supports both XML 1.1 and XML 1.0.
Returns "false" if the parser supports only XML 1.0.
|
Support for the default values of the
namespaces and namespace-prefixes
properties is required.
Support for any other feature flags is entirely optional.
For default values not specified by SAX2,
each XMLReader implementation specifies its default,
or may choose not to expose the feature flag.
Unless otherwise specified here,
implementations may support changing current values
of these standard feature flags, but not while parsing.
SAX2 Standard Handler and Property IDs
For parser interface characteristics that are described
as objects, a separate namespace is defined. The
objects in this namespace are again identified by URI, and
the standard property URIs have the prefix
http://xml.org/sax/properties/ before an identifier such as
lexical-handler or
dom-node . Manage those properties using
setProperty(). Those identifiers are:
Property ID |
Description |
declaration-handler |
Used to see most DTD declarations except those treated
as lexical ("document element name is ...") or which are
mandatory for all SAX parsers (DTDHandler).
The Object must implement org.xml.sax.ext.DeclHandler.
|
document-xml-version |
May be examined only during a parse, after the startDocument()
callback has been completed; read-only. This property is a
literal string describing the actual XML version of the document,
such as "1.0" or "1.1".
|
dom-node |
For "DOM Walker" style parsers, which ignore their
parser.parse() parameters, this is used to
specify the DOM (sub)tree being walked by the parser.
The Object must implement the
org.w3c.dom.Node interface.
|
lexical-handler |
Used to see some syntax events that are essential in some
applications: comments, CDATA delimiters, selected general
entity inclusions, and the start and end of the DTD
(and declaration of document element name).
The Object must implement org.xml.sax.ext.LexicalHandler.
|
xml-string |
Readable only during a parser callback, this exposes a TBS
chunk of characters responsible for the current event. |
All of these standard properties are optional;
XMLReader implementations need not support them.
|
|