package adql.query.constraint; /* * This file is part of ADQLLibrary. * * ADQLLibrary is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ADQLLibrary is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with ADQLLibrary. If not, see <http://www.gnu.org/licenses/>. * * Copyright 2012-2015 - UDS/Centre de DonnĂ©es astronomiques de Strasbourg (CDS), * Astronomisches Rechen Institute (ARI) */ import java.util.NoSuchElementException; import adql.query.ADQLIterator; import adql.query.ADQLList; import adql.query.ADQLObject; import adql.query.ADQLQuery; import adql.query.ClauseADQL; import adql.query.TextPosition; import adql.query.operand.ADQLOperand; /** * <p>It represents the IN predicate of SQL and ADQL.</p> * * <p>This predicate returns <i>true</i> if the value of the given operand is * either in the given values list or in the results of the given sub-query, else it returns <i>false</i>.</p> * * @author Grégory Mantelet (CDS;ARI) * @version 1.4 (06/2015) */ public class In implements ADQLConstraint { /** The operand whose the value must be in the given list or in the results of the given sub-query. */ private ADQLOperand leftOp; /** The sub-query which must return a list of values. */ private ADQLQuery subQuery; /** The list of values. */ private ADQLList<ADQLOperand> list; /** IN or NOT IN ? */ private boolean notIn = false; /** Position of this {@link In} in the given ADQL query string. * @since 1.4 */ private TextPosition position = null; /** * Builds an IN constraint with a sub-query. * * @param op The operand whose the value must be in the results of the given sub-query. * @param query A sub-query. * @throws NullPointerException If the given operand and/or the given sub-query is <i>null</i>. */ public In(ADQLOperand op, ADQLQuery query) throws NullPointerException{ this(op, query, false); } /** * Builds an IN constraint with a sub-query. * * @param op The operand whose the value must be in the results of the given sub-query. * @param query A sub-query. * @param notIn <i>true</i> for NOT IN, <i>false</i> for IN. * @throws NullPointerException If the given operand and/or the given sub-query is <i>null</i>. */ public In(ADQLOperand op, ADQLQuery query, boolean notIn) throws NullPointerException{ setOperand(op); setSubQuery(query); setNotIn(notIn); } /** * Builds an IN constraint with a values list. * * @param op The operand whose the value must be in the given values list. * @param valuesList The values list. * @throws NullPointerException If the given operand is <i>null</i> and/or the given list is <i>null</i> or empty. */ public In(ADQLOperand op, ADQLOperand[] valuesList) throws NullPointerException{ this(op, valuesList, false); } /** * Builds an IN constraint with a values list. * * @param op The operand whose the value must be in the given values list. * @param valuesList The values list. * @param notIn <i>true</i> for NOT IN, <i>false</i> for IN. * @throws NullPointerException If the given operand is <i>null</i> and/or the given list is <i>null</i> or empty. */ public In(ADQLOperand op, ADQLOperand[] valuesList, boolean notIn) throws NullPointerException{ setOperand(op); setValuesList(valuesList); setNotIn(notIn); } /** * Builds an IN constraint with a values list. * * @param op The operand whose the value must be in the given values list. * @param valuesList The values list. * @throws NullPointerException If the given operand is <i>null</i> and/or the given list is <i>null</i> or empty. */ public In(ADQLOperand op, ADQLList<ADQLOperand> valuesList) throws NullPointerException{ this(op, valuesList, false); } /** * Builds an IN constraint with a values list. * * @param op The operand whose the value must be in the given values list. * @param valuesList The values list. * @param notIn <i>true</i> for NOT IN, <i>false</i> for IN. * @throws NullPointerException If the given operand is <i>null</i> and/or the given list is <i>null</i> or empty. */ public In(ADQLOperand op, ADQLList<ADQLOperand> valuesList, boolean notIn) throws NullPointerException{ setOperand(op); setValuesList(valuesList); setNotIn(notIn); } /** * Builds a IN constraint by copying the given one. * * @param toCopy The IN constraint to copy. * @throws Exception If there is an error during the copy. */ @SuppressWarnings("unchecked") public In(In toCopy) throws Exception{ leftOp = (ADQLOperand)toCopy.leftOp.getCopy(); if (toCopy.hasSubQuery()) setSubQuery((ADQLQuery)toCopy.subQuery.getCopy()); else setValuesList((ADQLList<ADQLOperand>)toCopy.list.getCopy()); notIn = toCopy.notIn; position = (toCopy.position == null) ? null : new TextPosition(toCopy.position); } /** * Gets the left operand of this IN constraint. * * @return Its left operand. */ public final ADQLOperand getOperand(){ return leftOp; } /** * Replaces the left operand of this IN constraint. * * @param newLeftOp Its new left operand. * @throws NullPointerException If the given operand is <i>null</i>. */ public void setOperand(ADQLOperand newLeftOp) throws NullPointerException{ if (newLeftOp == null) throw new NullPointerException("Impossible to set a left operand NULL in an IN constraint !"); else{ leftOp = newLeftOp; position = null; } } /** * Gets the sub-query (right operand) of this IN constraint. * * @return Its sub-query. */ public final ADQLQuery getSubQuery(){ return subQuery; } /** * Tells whether the right operand of this IN constraint is a sub-query or a values list. * * @return <i>true</i> if the right operand is a sub-query, <i>false</i> for a values list (even empty). */ public final boolean hasSubQuery(){ return subQuery != null; } /** * Replaces the sub-query (right operand) of this IN constraint. * * @param newSubQuery Its new sub-query. * @throws NullPointerException If the given sub-query is <i>null</i>. */ public void setSubQuery(ADQLQuery newSubQuery) throws NullPointerException{ if (newSubQuery == null) throw new NullPointerException("Impossible to set a sub-query NULL in an IN constraint !"); else{ list = null; subQuery = newSubQuery; position = null; } } /** * Gets the values list (right operand) of this IN constraint. * * @return Its values list. */ public final ADQLList<ADQLOperand> getValuesList(){ return list; } /** * Replaces the values list (right operand) of this IN constraint. * * @param valuesList Its new values list. * @throws NullPointerException If the given list is <i>null</i>. */ public void setValuesList(ADQLOperand[] valuesList) throws NullPointerException{ if (valuesList == null) throw new NullPointerException("Impossible to set a values list NULL in an IN constraint !"); else if (valuesList.length > 0){ subQuery = null; list = new ClauseADQL<ADQLOperand>(); for(int i = 0; i < valuesList.length; i++) list.add(valuesList[i]); position = null; } } /** * Replaces the values list (right operand) of this IN constraint. * * @param valuesList Its new values list. * @throws NullPointerException If the given list is <i>null</i>. */ public void setValuesList(ADQLList<ADQLOperand> valuesList) throws NullPointerException{ if (valuesList == null) throw new NullPointerException("Impossible to set a values list NULL in an IN constraint !"); else{ subQuery = null; list = valuesList; position = null; } } /** * Tells whether this predicate is IN or NOT IN. * * @return <i>true</i> for NOT IN, <i>false</i> for IN. */ public final boolean isNotIn(){ return notIn; } /** * Lets telling whether this predicate must be IN or NOT IN. * * @param notIn <i>true</i> for NOT IN, <i>false</i> for IN. */ public void setNotIn(boolean notIn){ this.notIn = notIn; position = null; } @Override public final TextPosition getPosition(){ return position; } /** * Set the position of this {@link In} in the given ADQL query string. * * @param position New position of this {@link In}. * @since 1.4 */ public final void setPosition(final TextPosition position){ this.position = position; } @Override public ADQLObject getCopy() throws Exception{ return new In(this); } @Override public String getName(){ return notIn ? "NOT IN" : "IN"; } @Override public ADQLIterator adqlIterator(){ return new ADQLIterator(){ private int index = -1; @Override public ADQLObject next(){ index++; if (index == 0) return leftOp; else if (index == 1) return hasSubQuery() ? subQuery : list; else throw new NoSuchElementException(); } @Override public boolean hasNext(){ return index + 1 < 2; } @Override @SuppressWarnings("unchecked") public void replace(ADQLObject replacer) throws UnsupportedOperationException, IllegalStateException{ if (index <= -1) throw new IllegalStateException("replace(ADQLObject) impossible: next() has not yet been called !"); if (replacer == null) remove(); if (index == 0){ if (replacer instanceof ADQLOperand){ leftOp = (ADQLOperand)replacer; position = null; }else throw new UnsupportedOperationException("Impossible to replace an ADQLOperand by a " + replacer.getClass().getName() + " (" + replacer.toADQL() + ") !"); }else if (index == 1){ if (hasSubQuery() && replacer instanceof ADQLQuery){ subQuery = (ADQLQuery)replacer; position = null; }else if (!hasSubQuery() && replacer instanceof ADQLList){ list = (ADQLList<ADQLOperand>)replacer; position = null; }else throw new UnsupportedOperationException("Impossible to replace an " + (hasSubQuery() ? "ADQLQuery" : "ADQLList<ADQLOperand>") + " by a " + replacer.getClass().getName() + " (" + replacer.toADQL() + ") !"); } } @Override public void remove(){ if (index <= -1) throw new IllegalStateException("remove() impossible: next() has not yet been called !"); if (index == 0) throw new UnsupportedOperationException("Impossible to remove the left operand of the IN constraint !"); else if (index == 1) throw new UnsupportedOperationException("Impossible to remove the " + (hasSubQuery() ? "sub-query" : "values list") + " of the IN constraint !"); } }; } @Override public String toADQL(){ return leftOp.toADQL() + " " + getName() + " (" + (hasSubQuery() ? subQuery.toADQL() : list.toADQL()) + ")"; } }