/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/glitter/com.ibm.adtech.glitter/src/com/ibm/adtech/glitter/syntax/abstrakt/Optional.java,v $
* Created by: Lee Feigenbaum (<a href="mailto:feigenbl@us.ibm.com">feigenbl@us.ibm.com</a>)
* Created on: 10/23/06
* Revision: $Id: Optional.java 198 2007-08-01 16:25:33Z mroy $
*
* Contributors: IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.glitter.syntax.abstrakt;
import java.util.ArrayList;
import java.util.Collection;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import org.openanzo.exceptions.ExceptionConstants;
import org.openanzo.glitter.exception.GlitterRuntimeException;
import org.openanzo.glitter.query.QueryController;
import org.openanzo.glitter.query.QueryController.QueryStringPrintOptions;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.Variable;
/**
* Represents an OPTIONAL pattern in a SPARQL query, which consists possibly of a pattern that must match the underlying data, and a pattern that optionally
* might match the underlying data.
*
* @author lee <lee@cambridgesemantics.com>
*
*/
public class Optional extends GraphPattern {
/*
* binary operator modeling:
*/
private GraphPattern mustMatch;
private GraphPattern mayMatch;
// these filters only affect whether mayMatch can extend mustMatch, but
// they are evaluated against solutions that join solutions of mustMatch
// with solutions of mayMatch
private HashSet<Expression> filters;
@Override
public Optional clone() {
return new Optional(this.mustMatch != null ? (GraphPattern) this.mustMatch.clone() : null, (GraphPattern) this.mayMatch.clone(), this.filters);
}
/**
* Constructs an {@link Optional} from a pattern that must match, one that optionally matches, and a collection of filter expressions.
*
* @param mustMatch
* @param mayMatch
* @param filters
*/
protected Optional(GraphPattern mustMatch, GraphPattern mayMatch, Iterable<Expression> filters) {
this.mustMatch = mustMatch;
this.mayMatch = mayMatch;
this.filters = new HashSet<Expression>();
for (Expression e : filters)
this.filters.add(e);
if (this.mustMatch != null)
this.mustMatch.setParent(this);
this.mayMatch.setParent(this);
}
/**
* Constructs an {@link Optional} only from an optional pattern.
*
* @param mayMatch
*/
public Optional(GraphPattern mayMatch) {
this.mustMatch = null;
this.mayMatch = mayMatch;
this.filters = new HashSet<Expression>();
this.mayMatch.setParent(this);
}
@Override
public String toString() {
return (this.mustMatch != null ? this.mustMatch : "") + " OPTIONAL { " + this.mayMatch + " }";
}
public void prettyPrintQueryPart(EnumSet<QueryStringPrintOptions> printFlags, int indentLevel, Map<String, String> uri2prefix, StringBuilder s) {
if (this.mustMatch != null) {
this.mustMatch.prettyPrintQueryPart(printFlags, indentLevel, uri2prefix, s);
s.append(". ");
QueryController.printSeparator(printFlags, indentLevel, s);
}
s.append("OPTIONAL {");
indentLevel++;
QueryController.printSeparator(printFlags, indentLevel, s);
this.mayMatch.prettyPrintQueryPart(printFlags, indentLevel, uri2prefix, s);
indentLevel--;
QueryController.printSeparator(printFlags, indentLevel, s);
s.append("}");
}
@Override
public List<GraphPattern> getChildren() {
ArrayList<GraphPattern> ar = new ArrayList<GraphPattern>();
ar.add(mustMatch);
ar.add(mayMatch);
return ar;
}
/**
*
* @return The required part of the OPTIONAL.
*/
public GraphPattern getMustMatchPattern() {
return this.mustMatch;
}
/**
*
* @return The optional part of the OPTIONAL
*/
public GraphPattern getMayMatchPattern() {
return this.mayMatch;
}
@Override
public java.util.Set<Expression> getFilters() {
return this.filters;
}
@Override
public boolean replaceChild(TreeNode oldChild, TreeNode newChild) {
if (newChild instanceof GraphPattern) {
if (oldChild == this.mustMatch) {
if (oldChild != null)
oldChild.setParent(null);
this.mustMatch = (GraphPattern) newChild;
return true;
} else if (oldChild == this.mayMatch) {
newChild.setParent(this);
oldChild.setParent(null);
this.mayMatch = (GraphPattern) newChild;
return true;
}
}
return false;
}
@Override
public boolean removeChild(TreeNode child) {
if (child == this.mustMatch) {
this.mustMatch = null;
return true;
}
return false;
}
@Override
public void addChild(TreeNode child) {
if (child instanceof BGP) {
BGP bgp = (BGP) child;
if (bgp.getFunctionalPredicate() != null) {
if (this.mustMatch instanceof BGP) {
((BGP) this.mustMatch).setFunctionalPredicate(bgp.getFunctionalPredicate());
child.setParent(this);
}
return;
}
}
throw new GlitterRuntimeException(ExceptionConstants.GLITTER.NO_ADD_CHILD, this.getClass().getName());
}
@Override
protected Map<Variable, Integer> getVariableCount(boolean onlyBindableVariables) {
Map<Variable, Integer> vars = super.getVariableCount(onlyBindableVariables);
// if we want all variables, regardless of whether they can be bound to,
// we also include variables occuring inside filters
if (!onlyBindableVariables)
for (Expression e : this.filters)
for (Variable v : e.getReferencedVariables())
incrementVariableCount(vars, v, 1);
return vars;
}
@Override
public Collection<URI> getReferencedURIs() {
Collection<URI> uris = super.getReferencedURIs();
for (Expression e : this.filters)
uris.addAll(e.getReferencedURIs());
return uris;
}
}