001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.apache.commons.jxpath.ri.model.beans; 018 019import java.util.Locale; 020 021import org.apache.commons.jxpath.JXPathInvalidAccessException; 022import org.apache.commons.jxpath.ri.Compiler; 023import org.apache.commons.jxpath.ri.QName; 024import org.apache.commons.jxpath.ri.compiler.NodeNameTest; 025import org.apache.commons.jxpath.ri.compiler.NodeTest; 026import org.apache.commons.jxpath.ri.compiler.NodeTypeTest; 027import org.apache.commons.jxpath.ri.model.NodeIterator; 028import org.apache.commons.jxpath.ri.model.NodePointer; 029import org.apache.commons.jxpath.util.ValueUtils; 030 031/** 032 * A pointer describing a node that has properties, each of which could be 033 * a collection. 034 * 035 * @author Dmitri Plotnikov 036 * @version $Revision: 652845 $ $Date: 2008-05-02 19:46:46 +0200 (Fr, 02 Mai 2008) $ 037 */ 038public abstract class PropertyOwnerPointer extends NodePointer { 039 private static final Object UNINITIALIZED = new Object(); 040 041 private Object value = UNINITIALIZED; 042 043 public NodeIterator childIterator(NodeTest test, boolean reverse, 044 NodePointer startWith) { 045 if (test == null) { 046 return createNodeIterator(null, reverse, startWith); 047 } 048 if (test instanceof NodeNameTest) { 049 NodeNameTest nodeNameTest = (NodeNameTest) test; 050 QName testName = nodeNameTest.getNodeName(); 051 if (isValidProperty(testName)) { 052 return createNodeIterator(nodeNameTest.isWildcard() ? null 053 : testName.toString(), reverse, startWith); 054 } 055 return null; 056 } 057 return test instanceof NodeTypeTest && ((NodeTypeTest) test).getNodeType() == Compiler.NODE_TYPE_NODE 058 ? createNodeIterator(null, reverse, startWith) : null; 059 } 060 061 /** 062 * Create a NodeIterator. 063 * @param property property name 064 * @param reverse whether to iterate in reverse 065 * @param startWith first pointer to return 066 * @return NodeIterator 067 */ 068 public NodeIterator createNodeIterator(String property, boolean reverse, 069 NodePointer startWith) { 070 return new PropertyIterator(this, property, reverse, startWith); 071 } 072 073 public NodeIterator attributeIterator(QName name) { 074 return new BeanAttributeIterator(this, name); 075 } 076 077 /** 078 * Create a new PropertyOwnerPointer. 079 * @param parent parent pointer 080 * @param locale Locale 081 */ 082 protected PropertyOwnerPointer(NodePointer parent, Locale locale) { 083 super(parent, locale); 084 } 085 086 /** 087 * Create a new PropertyOwnerPointer. 088 * @param parent pointer 089 */ 090 protected PropertyOwnerPointer(NodePointer parent) { 091 super(parent); 092 } 093 094 public void setIndex(int index) { 095 if (this.index != index) { 096 super.setIndex(index); 097 value = UNINITIALIZED; 098 } 099 } 100 101 public Object getImmediateNode() { 102 if (value == UNINITIALIZED) { 103 value = index == WHOLE_COLLECTION ? ValueUtils.getValue(getBaseValue()) 104 : ValueUtils.getValue(getBaseValue(), index); 105 } 106 return value; 107 } 108 109 public abstract QName getName(); 110 111 /** 112 * Learn whether <code>name</code> is a valid child name for this PropertyOwnerPointer. 113 * @param name the QName to test 114 * @return <code>true</code> if <code>QName</code> is a valid property name. 115 * @since JXPath 1.3 116 */ 117 public boolean isValidProperty(QName name) { 118 return isDefaultNamespace(name.getPrefix()); 119 } 120 121 /** 122 * Throws an exception if you try to change the root element, otherwise 123 * forwards the call to the parent pointer. 124 * @param value to set 125 */ 126 public void setValue(Object value) { 127 this.value = value; 128 if (parent != null) { 129 if (parent.isContainer()) { 130 parent.setValue(value); 131 } 132 else { 133 if (index == WHOLE_COLLECTION) { 134 throw new UnsupportedOperationException( 135 "Cannot setValue of an object that is not " 136 + "some other object's property"); 137 } 138 throw new JXPathInvalidAccessException( 139 "The specified collection element does not exist: " + this); 140 } 141 } 142 else { 143 throw new UnsupportedOperationException( 144 "Cannot replace the root object"); 145 } 146 } 147 148 /** 149 * If this is a root node pointer, throws an exception; otherwise 150 * forwards the call to the parent node. 151 */ 152 public void remove() { 153 this.value = null; 154 if (parent != null) { 155 parent.remove(); 156 } 157 else { 158 throw new UnsupportedOperationException( 159 "Cannot remove an object that is not " 160 + "some other object's property or a collection element"); 161 } 162 } 163 164 /** 165 * Get a PropertyPointer for this PropertyOwnerPointer. 166 * @return PropertyPointer 167 */ 168 public abstract PropertyPointer getPropertyPointer(); 169 170 /** 171 * Learn whether dynamic property declaration is supported. 172 * @return true if the property owner can set a property "does not exist". 173 * A good example is a Map. You can always assign a value to any 174 * key even if it has never been "declared". 175 */ 176 public boolean isDynamicPropertyDeclarationSupported() { 177 return false; 178 } 179 180 public int compareChildNodePointers(NodePointer pointer1, 181 NodePointer pointer2) { 182 int r = pointer1.getName().toString().compareTo(pointer2.getName().toString()); 183 return r == 0 ? pointer1.getIndex() - pointer2.getIndex() : r; 184 } 185}