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 */ 017 018package org.apache.commons.jxpath.ri.axes; 019 020import java.util.Iterator; 021 022import org.apache.commons.jxpath.ri.EvalContext; 023import org.apache.commons.jxpath.ri.InfoSetUtil; 024import org.apache.commons.jxpath.ri.compiler.Expression; 025import org.apache.commons.jxpath.ri.compiler.NameAttributeTest; 026import org.apache.commons.jxpath.ri.model.NodePointer; 027import org.apache.commons.jxpath.ri.model.beans.PropertyOwnerPointer; 028import org.apache.commons.jxpath.ri.model.beans.PropertyPointer; 029 030/** 031 * EvalContext that checks predicates. 032 */ 033public class PredicateContext extends EvalContext { 034 035 private final Expression expression; 036 private boolean done; 037 private Expression nameTestExpression; 038 private PropertyPointer dynamicPropertyPointer; 039 040 /** 041 * Constructs a new PredicateContext. 042 * 043 * @param parentContext parent context 044 * @param expression compiled Expression 045 */ 046 public PredicateContext(final EvalContext parentContext, final Expression expression) { 047 super(parentContext); 048 this.expression = expression; 049 if (expression instanceof NameAttributeTest) { 050 nameTestExpression = ((NameAttributeTest) expression).getNameTestExpression(); 051 } 052 } 053 054 @Override 055 public NodePointer getCurrentNodePointer() { 056 if (position == 0 && !setPosition(1)) { 057 return null; 058 } 059 if (dynamicPropertyPointer != null) { 060 return dynamicPropertyPointer.getValuePointer(); 061 } 062 return parentContext.getCurrentNodePointer(); 063 } 064 065 @Override 066 public boolean nextNode() { 067 if (done) { 068 return false; 069 } 070 while (parentContext.nextNode()) { 071 if (setupDynamicPropertyPointer()) { 072 final Object pred = nameTestExpression.computeValue(parentContext); 073 final String propertyName = InfoSetUtil.stringValue(pred); 074 // At this point it would be nice to say: 075 // dynamicPropertyPointer.setPropertyName(propertyName) 076 // and then: dynamicPropertyPointer.isActual(). 077 // However some PropertyPointers, e.g. DynamicPropertyPointer 078 // will declare that any property you ask for is actual. 079 // That's not acceptable for us: we really need to know 080 // if the property is currently declared. Thus, 081 // we'll need to perform a search. 082 boolean ok = false; 083 final String[] names = dynamicPropertyPointer.getPropertyNames(); 084 for (final String name : names) { 085 if (name.equals(propertyName)) { 086 ok = true; 087 break; 088 } 089 } 090 if (ok) { 091 dynamicPropertyPointer.setPropertyName(propertyName); 092 position++; 093 return true; 094 } 095 } else { 096 Object pred = expression.computeValue(parentContext); 097 if (pred instanceof Iterator) { 098 if (!((Iterator) pred).hasNext()) { 099 return false; 100 } 101 pred = ((Iterator) pred).next(); 102 } 103 if (pred instanceof NodePointer) { 104 pred = ((NodePointer) pred).getNode(); 105 } 106 if (pred instanceof Number) { 107 final int pos = (int) InfoSetUtil.doubleValue(pred); 108 position++; 109 done = true; 110 return parentContext.setPosition(pos); 111 } 112 if (InfoSetUtil.booleanValue(pred)) { 113 position++; 114 return true; 115 } 116 } 117 } 118 return false; 119 } 120 121 @Override 122 public boolean nextSet() { 123 reset(); 124 return parentContext.nextSet(); 125 } 126 127 @Override 128 public void reset() { 129 super.reset(); 130 parentContext.reset(); 131 done = false; 132 } 133 134 @Override 135 public boolean setPosition(final int position) { 136 if (nameTestExpression == null) { 137 return setPositionStandard(position); 138 } 139 if (dynamicPropertyPointer == null && !setupDynamicPropertyPointer()) { 140 return setPositionStandard(position); 141 } 142 if (position < 1 || position > dynamicPropertyPointer.getLength()) { 143 return false; 144 } 145 dynamicPropertyPointer.setIndex(position - 1); 146 return true; 147 } 148 149 /** 150 * Basic setPosition 151 * 152 * @param position to set 153 * @return whether valid 154 */ 155 private boolean setPositionStandard(final int position) { 156 if (this.position > position) { 157 reset(); 158 } 159 while (this.position < position) { 160 if (!nextNode()) { 161 return false; 162 } 163 } 164 return true; 165 } 166 167 /** 168 * Used for an optimized access to dynamic properties using the "map[@name = 'name']" syntax 169 * 170 * @return whether valid 171 */ 172 private boolean setupDynamicPropertyPointer() { 173 if (nameTestExpression == null) { 174 return false; 175 } 176 NodePointer parent = parentContext.getCurrentNodePointer(); 177 if (parent == null) { 178 return false; 179 } 180 parent = parent.getValuePointer(); 181 if (!(parent instanceof PropertyOwnerPointer)) { 182 return false; 183 } 184 dynamicPropertyPointer = (PropertyPointer) ((PropertyOwnerPointer) parent).getPropertyPointer().clone(); 185 return true; 186 } 187}