/*
* Copyright 2010 Outerthought bvba
*
* Licensed 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.lilyproject.indexer.model.indexerconf;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.lilyproject.repository.api.FieldType;
import org.lilyproject.repository.api.RepositoryException;
import org.lilyproject.repository.api.SchemaId;
import org.lilyproject.repository.api.TypeManager;
public class DerefValue extends BaseValue {
private List<Follow> follows = new ArrayList<Follow>();
private List<Follow> crossRecordFollows = new ArrayList<Follow>();
private Value value;
private FieldType targetField;
private FieldType lastRealField;
/**
* NOTE: targetField is the 'artificial' target field that takes the single/multivaluedness of the
* follows into account
*/
protected DerefValue(List<Follow> follows, Value value, FieldType targetField, boolean extractContent, String formatter) {
super(extractContent, formatter);
//NOTE: extractContent and formatter should be identical to the one for value
// so it doesn't matter which ones are used by the ValueEvaluator
this.follows = follows;
this.value = value;
this.targetField = targetField;
}
/**
* This method should be called after all follow-expressions have been added.
*/
protected void init(TypeManager typeManager) throws RepositoryException, InterruptedException {
follows = Collections.unmodifiableList(follows);
for (Follow follow: follows) {
if (follow instanceof MasterFollow
|| follow instanceof VariantFollow
|| follow instanceof ForwardVariantFollow
|| follow instanceof LinkFieldFollow) {
crossRecordFollows.add(follow);
}
}
crossRecordFollows = Collections.unmodifiableList(crossRecordFollows);
// To find the lastRealField:
// - run over the follows in inverse order
// - on encountering the first Follow which is not a RecordFieldFollow, take the field of the
// RecordFieldFollow which follows it, or the target field if we're at the last entry.
// - it is possible that we reach the end and only encountered RecordFieldFollows, in that
// case the lastRealField stays null
for (int i = follows.size() - 1; i >= 0; i--) {
if (follows.get(i) instanceof RecordFieldFollow) {
continue;
}
if (i == follows.size() - 1) {
lastRealField = targetField;
} else {
lastRealField = ((RecordFieldFollow) follows.get(i + 1)).getFieldType();
}
break;
}
//
// Set the ownerFieldType property for LinkFieldFollow's
//
RecordFieldFollow currentRootRecordFieldFollow = null;
for (Follow follow : follows) {
if (follow instanceof LinkFieldFollow) {
if (currentRootRecordFieldFollow != null) {
((LinkFieldFollow) follow).setOwnerFieldType(currentRootRecordFieldFollow.getFieldType());
}
currentRootRecordFieldFollow = null;
} else if (follow instanceof MasterFollow || follow instanceof VariantFollow ||
follow instanceof ForwardVariantFollow) {
if (currentRootRecordFieldFollow != null) {
throw new RuntimeException("Unexpected situation: master or variant follow after record" +
" follow: this should have been validated by the indexer conf parser.");
}
currentRootRecordFieldFollow = null;
} else if (follow instanceof RecordFieldFollow) {
if (currentRootRecordFieldFollow == null) {
currentRootRecordFieldFollow = (RecordFieldFollow) follow;
}
} else {
throw new RuntimeException("Unexpected follow impl: " + follow.getClass().getName());
}
}
}
protected void addLinkFieldFollow(FieldType fieldType) {
LinkFieldFollow follow = new LinkFieldFollow(fieldType);
follows.add(follow);
crossRecordFollows.add(follow);
}
protected void addRecordFieldFollow(FieldType fieldType) {
follows.add(new RecordFieldFollow(fieldType));
}
protected void addMasterFollow() {
MasterFollow follow = new MasterFollow();
follows.add(follow);
crossRecordFollows.add(follow);
}
protected void addVariantFollow(Set<String> dimensions) {
VariantFollow follow = new VariantFollow(dimensions);
follows.add(follow);
crossRecordFollows.add(follow);
}
protected void addForwardVariantFollow(Map<String, String> dimensions) {
ForwardVariantFollow follow = new ForwardVariantFollow(dimensions);
follows.add(follow);
crossRecordFollows.add(follow);
}
public List<Follow> getFollows() {
return follows;
}
public List<Follow> getCrossRecordFollows() {
return crossRecordFollows;
}
/**
* Returns the field taken from the document to which the follow-expressions point, thus the last
* field in the chain.
*/
public FieldType getTargetField() {
return targetField;
}
/**
* Returns the last field in the dereference chain which is not a field from an embedded record.
* This can be null, when the dereferencing only goes through RECORD fields.
*/
public FieldType getLastRealField() {
return lastRealField;
}
@Override
public SchemaId getFieldDependency() {
if (follows.get(0) instanceof LinkFieldFollow) {
return ((LinkFieldFollow) follows.get(0)).getFieldType().getId();
} else {
// A follow-variant is like a link to another document, but the link can never change as the
// identity of the document never changes. Therefore, there is no dependency on a field.
return null;
}
}
@Override
public FieldType getTargetFieldType() {
return targetField;
}
}