001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/ogcbase/PropertyPath.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    package org.deegree.ogcbase;
037    
038    import java.util.List;
039    
040    import org.deegree.datatypes.QualifiedName;
041    import org.deegree.framework.xml.NamespaceContext;
042    
043    /**
044     * Represents a subset of the XPath expression language as described in section 7.4.2 of the Web
045     * Feature Implementation Specification 1.1.0 (but is used by other OGC specifications as well).
046     * <p>
047     * This specification does not require a WFS implementation to support the full XPath language. In
048     * order to keep the implementation entry cost as low as possible, this specification mandates that
049     * a WFS implementation <b>must</b> support the following subset of the XPath language:
050     * <ol>
051     * <li>A WFS implementation <b>must</b> support <i>abbreviated relative location</i> paths.</li>
052     * <li>Relative location paths are composed of one or more <i>steps</i> separated by the path
053     * separator '/'.</li>
054     * <li>The first step of a relative location path <b>may</b> correspond to the root element of the
055     * feature property being referenced <b>or</b> to the root element of the feature type with the
056     * next step corresponding to the root element of the feature property being referenced</li>
057     * <li>Each subsequent step in the path <b>must</b> be composed of the abbreviated form of the
058     * <i>child::</i> axis specifier and the name of the feature property encoded as the principal node
059     * type of <i>element</i>. The abbreviated form of the <i>child::</i> axis specifier is to simply
060     * omit the specifier from the location step.</li>
061     * <li>Each step in the path may optionally contain a predicate composed of the predicate
062     * delimiters '[' and ']' and a number indicating which child of the context node is to be selected.
063     * This allows feature properties that may be repeated to be specifically referenced.</li>
064     * <li>The final step in a path may optionally be composed of the abbreviated form of the
065     * <i>attribute::</i> axis specifier, '@', and the name of a feature property encoded as the
066     * principal node type of <i>attribute::</i>.</li>
067     * </ol>
068     * <p>
069     *
070     * @author <a href="mailto:schneider@lat-lon.de">Markus Schneider </a>
071     * @author last edited by: $Author: mschneider $
072     *
073     * @version $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
074     *
075     * @see PropertyPathStep
076     */
077    public class PropertyPath implements Comparable<PropertyPath> {
078    
079        private List<PropertyPathStep> steps;
080    
081        /**
082         * Creates a new instance of <code>PropertyPath</code> with the specified steps.
083         *
084         * @param steps
085         *            property path steps, may not be null
086         */
087        public PropertyPath( List<PropertyPathStep> steps ) {
088            if ( steps.size() < 1 ) {
089                throw new IllegalArgumentException( "PropertyPath must contain at least one step." );
090            }
091            this.steps = steps;
092        }
093    
094        /**
095         * Returns the namespace bindings for the prefices that are used by this property path.
096         *
097         * @return the namespace bindings
098         */
099        public NamespaceContext getNamespaceContext() {
100            NamespaceContext nsContext = new NamespaceContext();
101            for ( PropertyPathStep step : steps ) {
102                QualifiedName elementName = step.getPropertyName();
103                if ( elementName.getPrefix() != null && elementName.getNamespace() != null ) {
104                    nsContext.addNamespace( elementName.getPrefix(), elementName.getNamespace() );
105                }
106            }
107            return nsContext;
108        }
109    
110        /**
111         * Returns the number of steps.
112         *
113         * @return the number of steps.
114         */
115        public int getSteps() {
116            return this.steps.size();
117        }
118    
119        /**
120         * Returns the canonical string representation.
121         *
122         * @return canonical string representation
123         */
124        public String getAsString() {
125            StringBuffer sb = new StringBuffer( 500 );
126            for ( int i = 0; i < steps.size(); i++ ) {
127                sb.append( steps.get( i ).toString() );
128                if ( i < steps.size() - 1 ) {
129                    sb.append( '/' );
130                }
131            }
132            return sb.toString();
133        }
134    
135        /**
136         * Returns the <code>PropertyPathStep</code> at the given index.
137         *
138         * @param i
139         * @return the <code>PropertyPathStep</code> at the given index
140         */
141        public PropertyPathStep getStep( int i ) {
142            return this.steps.get( i );
143        }
144    
145        /**
146         * Returns all steps of the <code>PropertyPath</code>.
147         *
148         * @return all steps of the <code>PropertyPath</code>
149         */
150        public List<PropertyPathStep> getAllSteps() {
151            return this.steps;
152        }
153    
154        /**
155         * Adds the given <code>PropertyPathStep</code> to the end of the path.
156         *
157         * @param last
158         *            <code>PropertyPathStep</code> to add
159         */
160        public void append( PropertyPathStep last ) {
161            this.steps.add( last );
162        }
163    
164        /**
165         * Adds the given <code>PropertyPathStep</code> to the beginning of the path.
166         *
167         * @param first
168         *            <code>PropertyPathStep</code> to add
169         */
170        public void prepend( PropertyPathStep first ) {
171            this.steps.add( 0, first );
172        }
173    
174        @Override
175        public int hashCode() {
176            int hashCode = 0;
177            for ( PropertyPathStep step : steps ) {
178                hashCode += step.hashCode();
179            }
180            return hashCode;
181        }
182    
183        @Override
184        public boolean equals( Object obj ) {
185            if ( !( obj instanceof PropertyPath ) ) {
186                return false;
187            }
188            PropertyPath that = (PropertyPath) obj;
189            if ( this.getSteps() != that.getSteps() ) {
190                return false;
191            }
192            for ( int i = 0; i < this.getSteps(); i++ ) {
193                if ( !this.getStep( i ).equals( that.getStep( i ) ) ) {
194                    return false;
195                }
196            }
197            return true;
198        }
199    
200        @Override
201        public String toString() {
202            StringBuffer sb = new StringBuffer();
203            for ( int i = 0; i < getSteps(); i++ ) {
204                sb.append( getStep( i ) );
205                if ( i != getSteps() - 1 ) {
206                    sb.append( "/" );
207                }
208            }
209            return sb.toString();
210        }
211    
212        /**
213         * Compares this object with the specified object for order.
214         * <p>
215         * TODO use really unique string representations (namespaces!) + cache them
216         *
217         * @param that
218         *            the PropertyPath to be compared
219         * @return a negative integer, zero, or a positive integer as this object is less than, equal
220         *         to, or greater than the specified object
221         */
222        public int compareTo( PropertyPath that ) {
223            return this.toString().compareTo( that.toString() );
224        }
225    }