All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
jlibs.xml.sax.dog.expr.nodset.LocationEvaluation Maven / Gradle / Ivy
/**
* Copyright 2015 Santhosh Kumar Tekuri
*
* The JLibs authors license 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 jlibs.xml.sax.dog.expr.nodset;
import jlibs.core.util.LongTreeMap;
import jlibs.xml.sax.dog.DataType;
import jlibs.xml.sax.dog.NodeItem;
import jlibs.xml.sax.dog.Scope;
import jlibs.xml.sax.dog.expr.Evaluation;
import jlibs.xml.sax.dog.expr.Expression;
import jlibs.xml.sax.dog.expr.LinkableEvaluation;
import jlibs.xml.sax.dog.path.AxisListener;
import jlibs.xml.sax.dog.path.EventID;
import jlibs.xml.sax.dog.path.Step;
import jlibs.xml.sax.dog.sniff.Event;
import java.util.ArrayList;
import java.util.List;
/**
* @author Santhosh Kumar T
*/
public final class LocationEvaluation extends AxisListener implements NodeSetListener.Support{
private final Event event;
private final EventID eventID;
private final int index;
private final boolean lastStep;
private final Step currentStep;
private final boolean exactPosition;
private Evaluation predicateEvaluation;
private Boolean predicateResult = Boolean.TRUE;
private PositionTracker positionTracker;
private int predicateChain = -1;
protected LocationEvaluation(LocationExpression expression, int stepIndex, Event event, EventID eventID){
super(expression, event.order());
this.event = event;
this.eventID = eventID;
this.index = stepIndex;
lastStep = index==expression.locationPath.steps.length-1;
if(expression instanceof Strings)
stringEvaluations = new ArrayList();
currentStep = expression.locationPath.steps[stepIndex];
exactPosition = currentStep.predicateSet.getPredicate() instanceof ExactPosition;
if(currentStep.predicateSet.hasPosition)
positionTracker = new PositionTracker(currentStep.predicateSet.headPositionalPredicate);
}
private LocationEvaluation(LocationExpression expression, int stepIndex, Event event, EventID eventID, Expression predicate, Evaluation predicateEvaluation){
this(expression, stepIndex, event, eventID);
predicateResult = null;
if(predicateEvaluation==null)
this.predicateEvaluation = event.addListener(predicate, this);
else{
this.predicateEvaluation = predicateEvaluation;
predicateEvaluation.addListener(this);
}
}
@Override
public void start(){
assert predicateResult!=Boolean.FALSE;
assert !finished;
if(event.hasInstantListener(expression)){
if(listener instanceof LocationEvaluation)
predicateChain = ((LocationEvaluation)listener).predicateChain;
else
predicateChain = 0;
if(predicateEvaluation!=null)
predicateChain++;
}
if(predicateEvaluation!=null){
Expression predicate = predicateEvaluation.expression;
if(predicate.scope()!=Scope.DOCUMENT)
predicateEvaluation.start();
}
eventID.addListener(event, currentStep, this);
}
private LinkableEvaluation pendingEvaluationHead, pendingEvaluationTail;
private List stringEvaluations;
@Override
public void onHit(EventID eventID){
assert !finished : "getting events even after finish";
final LocationExpression expression = this.expression;
if(!lastStep){
if(eventID.isEmpty(expression.locationPath.steps[index+1].axis))
return;
}
final Event event = this.event;
if(positionTracker!=null){
event.positionTrackerStack.addFirst(positionTracker);
positionTracker.addEvaluation(event);
}
LinkableEvaluation childEval = null;
Expression predicate = currentStep.predicateSet.getPredicate();
Object predicateResult = predicate==null ? Boolean.TRUE : event.evaluate(predicate);
if(predicateResult==Boolean.TRUE){
if(lastStep)
consume(event);
else
childEval = new LocationEvaluation(expression, index+1, event, eventID);
}else if(predicateResult==null){
Evaluation predicateEvaluation = event.evaluation;
if(lastStep){
childEval = new PredicateEvaluation(expression, event.order(), expression.getResultItem(event), event, predicate, predicateEvaluation);
if(nodeSetListener !=null)
nodeSetListener.mayHit();
}else
childEval = new LocationEvaluation(expression, index+1, event, eventID, predicate, predicateEvaluation);
}
if(childEval!=null){
if(childEval instanceof LocationEvaluation)
((LocationEvaluation)childEval).nodeSetListener = nodeSetListener;
else
((PredicateEvaluation)childEval).nodeSetListener = nodeSetListener;
childEval.addListener(this);
if(pendingEvaluationTail!=null){
pendingEvaluationTail.next = childEval;
childEval.previous = pendingEvaluationTail;
pendingEvaluationTail = childEval;
}else
pendingEvaluationHead = pendingEvaluationTail = childEval;
childEval.start();
}
if(positionTracker!=null){
positionTracker.startEvaluation();
event.positionTrackerStack.pollFirst();
}
if(exactPosition && predicateResult==Boolean.TRUE){
manuallyExpired = true;
expired();
}
}
/*-------------------------------------------------[ Stages ]---------------------------------------------------*/
private boolean expired = false;
@Override
public void expired(){
assert !expired;
expired = true;
if(positionTracker!=null)
positionTracker.expired();
if(pendingEvaluationHead==null)
resultPrepared();
}
private boolean resultPrepared = false;
private void resultPrepared(){
if(!resultPrepared){
manuallyExpired = true;
resultPrepared = true;
for(LinkableEvaluation pendingEval=pendingEvaluationHead; pendingEval!=null; pendingEval=pendingEval.next)
pendingEval.removeListener(this);
pendingEvaluationHead = pendingEvaluationTail = null;
}
if(predicateResult!=null && (index!=0 || (stringEvaluations==null || stringEvaluations.size()==0)))
finished();
else if(result.size()==0 && predicateResult==null){ // when result is empty, there is no need to wait for predicateEvaluation to finish
Expression predicate = predicateEvaluation.expression;
if(predicate.scope()!=Scope.DOCUMENT)
predicateEvaluation.removeListener(this);
else
event.removeListener(predicate, this);
finished();
}
}
private boolean finished = false;
private void finished(){
if(!finished){
finished = true;
for(LinkableEvaluation pendingEval=pendingEvaluationHead; pendingEval!=null; pendingEval=pendingEval.next)
pendingEval.removeListener(this);
pendingEvaluationHead = pendingEvaluationTail = null;
fireFinished();
}
}
@Override
protected void fireFinished(){
if(index==0 && nodeSetListener !=null)
nodeSetListener.finished();
super.fireFinished();
if(stringEvaluations!=null){
for(Evaluation stringEval: stringEvaluations)
stringEval.removeListener(this);
}
}
@Override
protected void dispose(){
if(nodeSetListener !=null){
for(LongTreeMap.Entry entry = result.firstEntry(); entry!=null ; entry = entry.next())
nodeSetListener.discard(entry.getKey());
}
manuallyExpired = true;
for(LinkableEvaluation pendingEval=pendingEvaluationHead; pendingEval!=null; pendingEval=pendingEval.next)
pendingEval.removeListener(this);
pendingEvaluationHead = pendingEvaluationTail = null;
if(predicateResult==null)
predicateEvaluation.removeListener(this);
}
/*-------------------------------------------------[ Result Management ]---------------------------------------------------*/
private LongTreeMap result = new LongTreeMap();
private void consumedResult(){
int resultSize = result.size();
if(resultSize>0 && !expression.many){
if(resultSize>1)
result.deleteEntry(result.lastEntry());
if(expression.first){
if(pendingEvaluationHead==null || result.firstEntry().getKey()<=pendingEvaluationHead.order)
resultPrepared();
else if(!expired){
manuallyExpired = true;
expired = true;
}
}else
resultPrepared();
}else if(expired && pendingEvaluationHead==null)
resultPrepared();
}
private void consume(Event event){
assert lastStep;
Object resultItem = expression.getResultItem(event);
if(resultItem instanceof Evaluation){
Evaluation eval = (Evaluation)resultItem;
stringEvaluations.add(eval);
eval.addListener(this);
eval.start();
}else if(predicateChain==0){
event.onInstantResult(expression, (NodeItem)resultItem);
resultItem = Event.DUMMY_VALUE;
}
assert resultItem!=null : "ResultItem should be non-null";
result.put(event.order(), resultItem);
consumedResult();
if(nodeSetListener !=null)
nodeSetListener.mayHit();
}
private void consumeChildEvaluation(long order, Object resultItem){
boolean prepareResult = false;
if(expression.resultType==DataType.NUMBER){
if(resultItem instanceof Double && ((Double)resultItem).isNaN()){
result.clear();
prepareResult = true;
}
}
if(predicateChain==0){
event.onInstantResult(expression, (NodeItem)resultItem);
resultItem = Event.DUMMY_VALUE;
}
result.put(order, resultItem);
consumedResult();
if(prepareResult)
resultPrepared();
}
private void consumeChildEvaluation(LongTreeMap childResult){
boolean prepareResult = false;
int size = childResult.size();
if(size==1 && expression.resultType==DataType.NUMBER){
Object resultItem = childResult.firstEntry().value;
if(resultItem instanceof Double && ((Double)resultItem).isNaN()){
result.clear();
prepareResult = true;
}
}
if(size>0){
if(predicateChain==0)
fireInstantResult(childResult);
if(result.size()>0){
if(nodeSetListener !=null){
for(LongTreeMap.Entry entry = childResult.firstEntry(); entry!=null ; entry = entry.next()){
if(result.put(entry.getKey(), entry.value)!=null)
nodeSetListener.discard(entry.getKey());
}
}else
result.putAll(childResult);
}else
result = childResult;
}
consumedResult();
if(prepareResult)
resultPrepared();
}
private void remove(LinkableEvaluation eval){
LinkableEvaluation prev = eval.previous;
LinkableEvaluation next = eval.next;
if(prev!=null)
prev.next = next;
else
pendingEvaluationHead = next;
if(next!=null)
next.previous = prev;
else
pendingEvaluationTail = prev;
}
private void fireInstantResult(LongTreeMap result){
LongTreeMap.Entry entry = result.firstEntry();
while(entry!=null){
if(entry.value instanceof NodeItem){
event.onInstantResult(expression, (NodeItem)entry.value);
entry.value = Event.DUMMY_VALUE;
}
entry = entry.next();
}
result.clear(); // to avoid memory leak
}
private void decreasePredicateChain(){
predicateChain--;
for(LinkableEvaluation pendingEval=pendingEvaluationHead; pendingEval!=null; pendingEval=pendingEval.next){
if(pendingEval instanceof LocationEvaluation)
((LocationEvaluation)pendingEval).decreasePredicateChain();
}
if(predicateChain==0)
fireInstantResult(result);
}
@Override
public final void finished(Evaluation evaluation){
assert !finished : "can't consume evaluation result after finish";
if(evaluation==predicateEvaluation){
predicateResult = (Boolean)evaluation.getResult();
assert predicateResult!=null : "evaluation result should be non-null";
if(predicateResult==Boolean.FALSE){
if(nodeSetListener !=null){
for(LongTreeMap.Entry entry = result.firstEntry(); entry!=null ; entry = entry.next())
nodeSetListener.discard(entry.getKey());
}
result.clear();
if(stringEvaluations!=null){
for(Evaluation stringEval: stringEvaluations)
stringEval.removeListener(this);
stringEvaluations = null;
}
resultPrepared();
}else{
if(predicateChain!=-1)
decreasePredicateChain();
if(resultPrepared)
finished();
}
}else if(evaluation instanceof PredicateEvaluation){
PredicateEvaluation predicateEvaluation = (PredicateEvaluation)evaluation;
remove(predicateEvaluation);
if(predicateEvaluation.result!=null){
Object resultItem = predicateEvaluation.result;
if(resultItem instanceof Evaluation){
Evaluation stringEval = (Evaluation)resultItem;
stringEvaluations.add(stringEval);
stringEval.addListener(this);
}
consumeChildEvaluation(predicateEvaluation.order, resultItem);
}else{
if(nodeSetListener !=null)
nodeSetListener.discard(predicateEvaluation.order);
consumedResult();
}
}else if(evaluation instanceof LocationEvaluation){
LocationEvaluation locEval = (LocationEvaluation)evaluation;
remove(locEval);
if(locEval.stringEvaluations!=null){
for(Evaluation stringEval: locEval.stringEvaluations)
stringEval.addListener(this);
stringEvaluations.addAll(locEval.stringEvaluations);
}
boolean wasExpired = expired;
consumeChildEvaluation(locEval.result);
if(!wasExpired && expired){
assert !finished;
LinkableEvaluation eval = locEval.next;
while(eval!=null){
eval.removeListener(this);
remove(eval);
eval = eval.next;
}
if(pendingEvaluationHead==null)
resultPrepared();
}
}else{
stringEvaluations.remove(evaluation);
consumeChildEvaluation(evaluation.order, evaluation.getResult());
}
}
private Object finalResult;
public Object getResult(){
if(index==0 && predicateChain==0)
return null;
if(finalResult==null)
finalResult = expression.getResult(result);
return finalResult;
}
public NodeSetListener nodeSetListener;
@Override
public void setNodeSetListener(NodeSetListener nodeSetListener){
this.nodeSetListener = nodeSetListener;
}
}