alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Commons JXPath example source code file (NodePointer.java)

This example Commons JXPath source code file (NodePointer.java) is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Java - Commons JXPath tags/keywords

cannot, jxpathexception, locale, nodepointer, nodepointer, object, object, pointer, qname, qname, string, string, stringbuffer, util, whole_collection

The Commons JXPath NodePointer.java source code

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.commons.jxpath.ri.model;

import java.util.HashSet;
import java.util.Locale;

import org.apache.commons.jxpath.AbstractFactory;
import org.apache.commons.jxpath.JXPathContext;
import org.apache.commons.jxpath.JXPathException;
import org.apache.commons.jxpath.NodeSet;
import org.apache.commons.jxpath.Pointer;
import org.apache.commons.jxpath.ri.Compiler;
import org.apache.commons.jxpath.ri.JXPathContextReferenceImpl;
import org.apache.commons.jxpath.ri.NamespaceResolver;
import org.apache.commons.jxpath.ri.QName;
import org.apache.commons.jxpath.ri.compiler.NodeNameTest;
import org.apache.commons.jxpath.ri.compiler.NodeTest;
import org.apache.commons.jxpath.ri.compiler.NodeTypeTest;
import org.apache.commons.jxpath.ri.model.beans.NullPointer;

/**
 * Common superclass for Pointers of all kinds.  A NodePointer maps to
 * a deterministic XPath that represents the location of a node in an
 * object graph. This XPath uses only simple axes: child, namespace and
 * attribute and only simple, context-independent predicates.
 *
 * @author Dmitri Plotnikov
 * @version $Revision: 668329 $ $Date: 2008-06-16 16:59:48 -0500 (Mon, 16 Jun 2008) $
 */
public abstract class NodePointer implements Pointer {

    /** Whole collection index. */
    public static final int WHOLE_COLLECTION = Integer.MIN_VALUE;

    /** Constant to indicate unknown namespace */
    public static final String UNKNOWN_NAMESPACE = "<";

    /** Index for this NodePointer */
    protected int index = WHOLE_COLLECTION;

    private boolean attribute = false;
    private NamespaceResolver namespaceResolver;
    private transient Object rootNode;

    /**
     * Allocates an entirely new NodePointer by iterating through all installed
     * NodePointerFactories until it finds one that can create a pointer.
     * @param name QName
     * @param bean Object
     * @param locale Locale
     * @return NodePointer
     */
    public static NodePointer newNodePointer(
        QName name,
        Object bean,
        Locale locale) {
        NodePointer pointer = null;
        if (bean == null) {
            pointer = new NullPointer(name, locale);
            return pointer;
        }

        NodePointerFactory[] factories =
            JXPathContextReferenceImpl.getNodePointerFactories();
        for (int i = 0; i < factories.length; i++) {
            pointer = factories[i].createNodePointer(name, bean, locale);
            if (pointer != null) {
                return pointer;
            }
        }
        throw new JXPathException(
            "Could not allocate a NodePointer for object of "
                + bean.getClass());
    }

    /**
     * Allocates an new child NodePointer by iterating through all installed
     * NodePointerFactories until it finds one that can create a pointer.
     * @param parent pointer
     * @param name QName
     * @param bean Object
     * @return NodePointer
     */
    public static NodePointer newChildNodePointer(
        NodePointer parent,
        QName name,
        Object bean) {
        NodePointerFactory[] factories =
            JXPathContextReferenceImpl.getNodePointerFactories();
        for (int i = 0; i < factories.length; i++) {
            NodePointer pointer =
                factories[i].createNodePointer(parent, name, bean);
            if (pointer != null) {
                return pointer;
            }
        }
        throw new JXPathException(
            "Could not allocate a NodePointer for object of "
                + bean.getClass());
    }

    /** Parent pointer */
    protected NodePointer parent;

    /** Locale */
    protected Locale locale;

    /**
     * Create a new NodePointer.
     * @param parent Pointer
     */
    protected NodePointer(NodePointer parent) {
        this.parent = parent;
    }

    /**
     * Create a new NodePointer.
     * @param parent Pointer
     * @param locale Locale
     */
    protected NodePointer(NodePointer parent, Locale locale) {
        this.parent = parent;
        this.locale = locale;
    }

    /**
     * Get the NamespaceResolver associated with this NodePointer.
     * @return NamespaceResolver
     */
    public NamespaceResolver getNamespaceResolver() {
        if (namespaceResolver == null && parent != null) {
            namespaceResolver = parent.getNamespaceResolver();
        }
        return namespaceResolver;
    }

    /**
     * Set the NamespaceResolver for this NodePointer.
     * @param namespaceResolver NamespaceResolver
     */
    public void setNamespaceResolver(NamespaceResolver namespaceResolver) {
        this.namespaceResolver = namespaceResolver;
    }

    /**
     * Get the parent pointer.
     * @return NodePointer
     */
    public NodePointer getParent() {
        NodePointer pointer = parent;
        while (pointer != null && pointer.isContainer()) {
            pointer = pointer.getImmediateParentPointer();
        }
        return pointer;
    }

    /**
     * Get the immediate parent pointer.
     * @return NodePointer
     */
    public NodePointer getImmediateParentPointer() {
        return parent;
    }

    /**
     * Set to true if the pointer represents the "attribute::" axis.
     * @param attribute boolean
     */
    public void setAttribute(boolean attribute) {
        this.attribute = attribute;
    }

    /**
     * Returns true if the pointer represents the "attribute::" axis.
     * @return boolean
     */
    public boolean isAttribute() {
        return attribute;
    }

    /**
     * Returns true if this Pointer has no parent.
     * @return boolean
     */
    public boolean isRoot() {
        return parent == null;
    }

    /**
     * If true, this node does not have children
     * @return boolean
     */
    public abstract boolean isLeaf();

    /**
     * Learn whether this pointer is considered to be a node.
     * @return boolean
     * @deprecated Please use !isContainer()
     */
    public boolean isNode() {
        return !isContainer();
    }

    /**
     * If true, this node is auxiliary and can only be used as an intermediate in
     * the chain of pointers.
     * @return boolean
     */
    public boolean isContainer() {
        return false;
    }

    /**
     * If the pointer represents a collection, the index identifies
     * an element of that collection.  The default value of <code>index
     * is <code>WHOLE_COLLECTION, which just means that the pointer
     * is not indexed at all.
     * Note: the index on NodePointer starts with 0, not 1.
     * @return int
     */
    public int getIndex() {
        return index;
    }

    /**
     * Set the index of this NodePointer.
     * @param index int
     */
    public void setIndex(int index) {
        this.index = index;
    }

    /**
     * Returns <code>true if the value of the pointer is an array or
     * a Collection.
     * @return boolean
     */
    public abstract boolean isCollection();

    /**
     * If the pointer represents a collection (or collection element),
     * returns the length of the collection.
     * Otherwise returns 1 (even if the value is null).
     * @return int
     */
    public abstract int getLength();

    /**
     * By default, returns <code>getNode(), can be overridden to
     * return a "canonical" value, like for instance a DOM element should
     * return its string value.
     * @return Object value
     */
    public Object getValue() {
        NodePointer valuePointer = getValuePointer();
        if (valuePointer != this) {
            return valuePointer.getValue();
        }
        // Default behavior is to return the same as getNode()
        return getNode();
    }

    /**
     * If this pointer manages a transparent container, like a variable,
     * this method returns the pointer to the contents.
     * Only an auxiliary (non-node) pointer can (and should) return a
     * value pointer other than itself.
     * Note that you probably don't want to override
     * <code>getValuePointer() directly.  Override the
     * <code>getImmediateValuePointer() method instead.  The
     * <code>getValuePointer() method is calls
     * <code>getImmediateValuePointer() and, if the result is not
     * <code>this, invokes getValuePointer() recursively.
     * The idea here is to open all nested containers. Let's say we have a
     * container within a container within a container. The
     * <code>getValuePointer() method should then open all those
     * containers and return the pointer to the ultimate contents. It does so
     * with the above recursion.
     * @return NodePointer
     */
    public NodePointer getValuePointer() {
        NodePointer ivp = getImmediateValuePointer();
        return ivp == this ? this : ivp.getValuePointer();
    }

    /**
     * @see #getValuePointer()
     *
     * @return NodePointer is either <code>this or a pointer
     *   for the immediately contained value.
     */
    public NodePointer getImmediateValuePointer() {
        return this;
    }

    /**
     * An actual pointer points to an existing part of an object graph, even
     * if it is null. A non-actual pointer represents a part that does not exist
     * at all.
     * For instance consider the pointer "/address/street".
     * If both <em>address and street are not null,
     * the pointer is actual.
     * If <em>address is not null, but street is null,
     * the pointer is still actual.
     * If <em>address is null, the pointer is not actual.
     * (In JavaBeans) if <em>address is not a property of the root bean,
     * a Pointer for this path cannot be obtained at all - actual or otherwise.
     * @return boolean
     */
    public boolean isActual() {
        return index == WHOLE_COLLECTION || index >= 0 && index < getLength();
    }

    /**
     * Returns the name of this node. Can be null.
     * @return QName
     */
    public abstract QName getName();

    /**
     * Returns the value represented by the pointer before indexing.
     * So, if the node represents an element of a collection, this
     * method returns the collection itself.
     * @return Object value
     */
    public abstract Object getBaseValue();

    /**
     * Returns the object the pointer points to; does not convert it
     * to a "canonical" type.
     * @return Object node value
     * @deprecated 1.1 Please use getNode()
     */
    public Object getNodeValue() {
        return getNode();
    }

    /**
     * Returns the object the pointer points to; does not convert it
     * to a "canonical" type. Opens containers, properties etc and returns
     * the ultimate contents.
     * @return Object node
     */
    public Object getNode() {
        return getValuePointer().getImmediateNode();
    }

    /**
     * Get the root node.
     * @return Object value of this pointer's root (top parent).
     */
    public synchronized Object getRootNode() {
        if (rootNode == null) {
            rootNode = parent == null ? getImmediateNode() : parent.getRootNode();
        }
        return rootNode;
    }

    /**
     * Returns the object the pointer points to; does not convert it
     * to a "canonical" type.
     * @return Object node
     */
    public abstract Object getImmediateNode();

    /**
     * Converts the value to the required type and changes the corresponding
     * object to that value.
     * @param value the value to set
     */
    public abstract void setValue(Object value);

    /**
     * Compares two child NodePointers and returns a positive number,
     * zero or a positive number according to the order of the pointers.
     * @param pointer1 first pointer to be compared
     * @param pointer2 second pointer to be compared
     * @return int per Java comparison conventions
     */
    public abstract int compareChildNodePointers(
            NodePointer pointer1, NodePointer pointer2);

    /**
     * Checks if this Pointer matches the supplied NodeTest.
     * @param test the NodeTest to execute
     * @return true if a match
     */
    public boolean testNode(NodeTest test) {
        if (test == null) {
            return true;
        }
        if (test instanceof NodeNameTest) {
            if (isContainer()) {
                return false;
            }
            NodeNameTest nodeNameTest = (NodeNameTest) test;
            QName testName = nodeNameTest.getNodeName();
            QName nodeName = getName();
            if (nodeName == null) {
                return false;
            }

            String testPrefix = testName.getPrefix();
            String nodePrefix = nodeName.getPrefix();
            if (!equalStrings(testPrefix, nodePrefix)) {
                String testNS = getNamespaceURI(testPrefix);
                String nodeNS = getNamespaceURI(nodePrefix);
                if (!equalStrings(testNS, nodeNS)) {
                    return false;
                }
            }
            if (nodeNameTest.isWildcard()) {
                return true;
            }
            return testName.getName().equals(nodeName.getName());
        }
        return test instanceof NodeTypeTest
                && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE && isNode();
    }

    /**
     * Compare two strings, either of which may be null, for equality.
     * @param s1 the first String to compare
     * @param s2 the second String to compare
     * @return true if both Strings are null, same or equal
     */
    private static boolean equalStrings(String s1, String s2) {
        return s1 == s2 || s1 != null && s1.equals(s2);
    }

    /**
     *  Called directly by JXPathContext. Must create path and
     *  set value.
     *  @param context the owning JXPathContext
     *  @param value the new value to set
     *  @return created NodePointer
     */
    public NodePointer createPath(JXPathContext context, Object value) {
        setValue(value);
        return this;
    }

    /**
     * Remove the node of the object graph this pointer points to.
     */
    public void remove() {
        // It is a no-op

//        System.err.println("REMOVING: " + asPath() + " " + getClass());
//        printPointerChain();
    }

    /**
     * Called by a child pointer when it needs to create a parent object.
     * Must create an object described by this pointer and return
     * a new pointer that properly describes the new object.
     * @param context the owning JXPathContext
     * @return created NodePointer
     */
    public NodePointer createPath(JXPathContext context) {
        return this;
    }

    /**
     * Called by a child pointer if that child needs to assign the value
     * supplied in the createPath(context, value) call to a non-existent
     * node. This method may have to expand the collection in order to assign
     * the element.
     * @param context the owning JXPathCOntext
     * @param name the QName at which a child should be created
     * @param index child index.
     * @param value node value to set
     * @return created NodePointer
     */
    public NodePointer createChild(
        JXPathContext context,
        QName name,
        int index,
        Object value) {
        throw new JXPathException("Cannot create an object for path "
                + asPath() + "/" + name + "[" + (index + 1) + "]"
                + ", operation is not allowed for this type of node");
    }

    /**
     * Called by a child pointer when it needs to create a parent object for a
     * non-existent collection element. It may have to expand the collection,
     * then create an element object and return a new pointer describing the
     * newly created element.
     * @param context the owning JXPathCOntext
     * @param name the QName at which a child should be created
     * @param index child index.
     * @return created NodePointer
     */
    public NodePointer createChild(JXPathContext context, QName name, int index) {
        throw new JXPathException("Cannot create an object for path "
                + asPath() + "/" + name + "[" + (index + 1) + "]"
                + ", operation is not allowed for this type of node");
    }

    /**
     * Called to create a non-existing attribute
     * @param context the owning JXPathCOntext
     * @param name the QName at which an attribute should be created
     * @return created NodePointer
     */
    public NodePointer createAttribute(JXPathContext context, QName name) {
        throw new JXPathException("Cannot create an attribute for path "
                + asPath() + "/@" + name
                + ", operation is not allowed for this type of node");
    }

    /**
     * If the Pointer has a parent, returns the parent's locale; otherwise
     * returns the locale specified when this Pointer was created.
     * @return Locale for this NodePointer
     */
    public Locale getLocale() {
        if (locale == null && parent != null) {
            locale = parent.getLocale();
        }
        return locale;
    }

    /**
     * Check whether our locale matches the specified language.
     * @param lang String language to check
     * @return true if the selected locale name starts
     *              with the specified prefix <i>lang, case-insensitive.
     */
    public boolean isLanguage(String lang) {
        Locale loc = getLocale();
        String name = loc.toString().replace('_', '-');
        return name.toUpperCase(Locale.ENGLISH).startsWith(lang.toUpperCase(Locale.ENGLISH));
    }

    /**
     * Returns a NodeIterator that iterates over all children or all children
     * that match the given NodeTest, starting with the specified one.
     * @param test NodeTest to filter children
     * @param reverse specified iteration direction
     * @param startWith the NodePointer to start with
     * @return NodeIterator
     */
    public NodeIterator childIterator(
        NodeTest test,
        boolean reverse,
        NodePointer startWith) {
        NodePointer valuePointer = getValuePointer();
        return valuePointer == null || valuePointer == this ? null
                : valuePointer.childIterator(test, reverse, startWith);
    }

    /**
     * Returns a NodeIterator that iterates over all attributes of the current
     * node matching the supplied node name (could have a wildcard).
     * May return null if the object does not support the attributes.
     * @param qname the attribute name to test
     * @return NodeIterator
     */
    public NodeIterator attributeIterator(QName qname) {
        NodePointer valuePointer = getValuePointer();
        return valuePointer == null || valuePointer == this ? null
                : valuePointer.attributeIterator(qname);
    }

    /**
     * Returns a NodeIterator that iterates over all namespaces of the value
     * currently pointed at.
     * May return null if the object does not support the namespaces.
     * @return NodeIterator
     */
    public NodeIterator namespaceIterator() {
        return null;
    }

    /**
     * Returns a NodePointer for the specified namespace. Will return null
     * if namespaces are not supported.
     * Will return UNKNOWN_NAMESPACE if there is no such namespace.
     * @param namespace incoming namespace
     * @return NodePointer for <code>namespace
     */
    public NodePointer namespacePointer(String namespace) {
        return null;
    }

    /**
     * Decodes a namespace prefix to the corresponding URI.
     * @param prefix prefix to decode
     * @return String uri
     */
    public String getNamespaceURI(String prefix) {
        return null;
    }

    /**
     * Returns the namespace URI associated with this Pointer.
     * @return String uri
     */
    public String getNamespaceURI() {
        return null;
    }

    /**
     * Returns true if the supplied prefix represents the
     * default namespace in the context of the current node.
     * @param prefix the prefix to check
     * @return <code>true if prefix is default
     */
    protected boolean isDefaultNamespace(String prefix) {
        if (prefix == null) {
            return true;
        }

        String namespace = getNamespaceURI(prefix);
        return namespace != null && namespace.equals(getDefaultNamespaceURI());
    }

    /**
     * Get the default ns uri
     * @return String uri
     */
    protected String getDefaultNamespaceURI() {
        return null;
    }

    /**
     * Locates a node by ID.
     * @param context JXPathContext owning context
     * @param id String id
     * @return Pointer found
     */
    public Pointer getPointerByID(JXPathContext context, String id) {
        return context.getPointerByID(id);
    }

    /**
     * Locates a node by key and value.
     * @param context owning JXPathContext
     * @param key key to search for
     * @param value value to match
     * @return Pointer found
     */
    public Pointer getPointerByKey(
            JXPathContext context,
            String key,
            String value) {
        return context.getPointerByKey(key, value);
    }

    /**
     * Find a NodeSet by key/value.
     * @param context owning JXPathContext
     * @param key key to search for
     * @param value value to match
     * @return NodeSet found
     */
    public NodeSet getNodeSetByKey(JXPathContext context, String key, Object value) {
        return context.getNodeSetByKey(key, value);
    }

    /**
     * Returns an XPath that maps to this Pointer.
     * @return String xpath expression
     */
    public String asPath() {
        // If the parent of this node is a container, it is responsible
        // for appended this node's part of the path.
        if (parent != null && parent.isContainer()) {
            return parent.asPath();
        }

        StringBuffer buffer = new StringBuffer();
        if (parent != null) {
            buffer.append(parent.asPath());
        }

        if (buffer.length() == 0
            || buffer.charAt(buffer.length() - 1) != '/') {
            buffer.append('/');
        }
        if (attribute) {
            buffer.append('@');
        }
        buffer.append(getName());

        if (index != WHOLE_COLLECTION && isCollection()) {
            buffer.append('[').append(index + 1).append(']');
        }
        return buffer.toString();
    }

    /**
     * Clone this NodePointer.
     * @return cloned NodePointer
     */
    public Object clone() {
        try {
            NodePointer ptr = (NodePointer) super.clone();
            if (parent != null) {
                ptr.parent = (NodePointer) parent.clone();
            }
            return ptr;
        }
        catch (CloneNotSupportedException ex) {
            // Of course it is supported
            ex.printStackTrace();
        }
        return null;
    }

    public String toString() {
        return asPath();
    }

    public int compareTo(Object object) {
        if (object == this) {
            return 0;
        }
        // Let it throw a ClassCastException
        NodePointer pointer = (NodePointer) object;
        if (parent == pointer.parent) {
            return parent == null ? 0 : parent.compareChildNodePointers(this, pointer);
        }

        // Task 1: find the common parent
        int depth1 = 0;
        NodePointer p1 = this;
        HashSet parents1 = new HashSet();
        while (p1 != null) {
            depth1++;
            p1 = p1.parent;
            if (p1 != null) {
                parents1.add(p1);
            }
        }
        boolean commonParentFound = false;
        int depth2 = 0;
        NodePointer p2 = pointer;
        while (p2 != null) {
            depth2++;
            p2 = p2.parent;
            if (parents1.contains(p2)) {
                commonParentFound = true;
            }
        }
        //nodes from different graphs are equal, else continue comparison:
        return commonParentFound ? compareNodePointers(this, depth1, pointer, depth2) : 0;
    }

    /**
     * Compare node pointers.
     * @param p1 pointer 1
     * @param depth1 depth 1
     * @param p2 pointer 2
     * @param depth2 depth 2
     * @return comparison result: (< 0) -> (p1 lt p2); (0) -> (p1 eq p2); (> 0) -> (p1 gt p2)
     */
    private int compareNodePointers(
        NodePointer p1,
        int depth1,
        NodePointer p2,
        int depth2) {
        if (depth1 < depth2) {
            int r = compareNodePointers(p1, depth1, p2.parent, depth2 - 1);
            return r == 0 ? -1 : r;
        }
        if (depth1 > depth2) {
            int r = compareNodePointers(p1.parent, depth1 - 1, p2, depth2);
            return r == 0 ? 1 : r;
        }
        //henceforth depth1 == depth2:
        if (p1 == p2 || p1 != null && p1.equals(p2)) {
            return 0;
        }
        if (depth1 == 1) {
            throw new JXPathException(
                    "Cannot compare pointers that do not belong to the same tree: '"
                    + p1 + "' and '" + p2 + "'");
        }
        int r = compareNodePointers(p1.parent, depth1 - 1, p2.parent, depth2 - 1);
        return r == 0 ? p1.parent.compareChildNodePointers(p1, p2) : r;
    }

    /**
     * Print internal structure of a pointer for debugging
     */
    public void printPointerChain() {
        printDeep(this, "");
    }

    /**
     * Return a string escaping single and double quotes.
     * @param string string to treat
     * @return string with any necessary changes made.
     */
    protected String escape(String string) {
        final char[] c = new char[] { '\'', '"' };
        final String[] esc = new String[] { "'", """ };
        StringBuffer sb = null;
        for (int i = 0; sb == null && i < c.length; i++) {
            if (string.indexOf(c[i]) >= 0) {
                sb = new StringBuffer(string);
            }
        }
        if (sb == null) {
            return string;
        }
        for (int i = 0; i < c.length; i++) {
            if (string.indexOf(c[i]) < 0) {
                continue;
            }
            int pos = 0;
            while (pos < sb.length()) {
                if (sb.charAt(pos) == c[i]) {
                    sb.replace(pos, pos + 1, esc[i]);
                    pos += esc[i].length();
                }
                else {
                    pos++;
                }
            }
        }
        return sb.toString();
    }

    /**
     * Get the AbstractFactory associated with the specified JXPathContext.
     * @param context JXPathContext
     * @return AbstractFactory
     */
    protected AbstractFactory getAbstractFactory(JXPathContext context) {
        AbstractFactory factory = context.getFactory();
        if (factory == null) {
            throw new JXPathException(
                "Factory is not set on the JXPathContext - cannot create path: "
                    + asPath());
        }
        return factory;
    }

    /**
     * Print deep
     * @param pointer to print
     * @param indent indentation level
     */
    private static void printDeep(NodePointer pointer, String indent) {
        if (indent.length() == 0) {
            System.err.println(
                "POINTER: "
                    + pointer
                    + "("
                    + pointer.getClass().getName()
                    + ")");
        }
        else {
            System.err.println(
                indent
                    + " of "
                    + pointer
                    + "("
                    + pointer.getClass().getName()
                    + ")");
        }
        if (pointer.getImmediateParentPointer() != null) {
            printDeep(pointer.getImmediateParentPointer(), indent + "  ");
        }
    }
}

Other Commons JXPath examples (source code examples)

Here is a short list of links related to this Commons JXPath NodePointer.java source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.