001/*
002 *  Copyright 2015 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.cms.search.query;
017
018import java.util.Arrays;
019
020import org.ametys.cms.content.indexing.solr.SolrFieldNames;
021
022
023/**
024 * Represents a {@link Query} testing if an object possesses one or more tags.
025 */
026public class TagQuery implements Query
027{
028    
029    private Operator _operator;
030    private String[] _values;
031    private LogicalOperator _logicalOperator;
032    private boolean _descendantAutoposting;
033    
034    /**
035     * Build a TagQuery.
036     * @param value the tag id to test.
037     */
038    public TagQuery(String value)
039    {
040        this(new String[] {value});
041    }
042    
043    /**
044     * Build a TagQuery.
045     * @param values the tag IDs to test.
046     */
047    public TagQuery(String... values)
048    {
049        this(Operator.EQ, values);
050    }
051    
052    /**
053     * Build a TagQuery.
054     * @param operator the operator.
055     * @param value the tag id to test.
056     */
057    public TagQuery(Operator operator, String value)
058    {
059        this(operator, new String[] {value});
060    }
061    
062    /**
063     * Build a TagQuery.
064     * @param operator the operator.
065     * @param values the tag IDs to test.
066     */
067    public TagQuery(Operator operator, String... values)
068    {
069        this(operator, false, values);
070    }
071    
072    /**
073     * Build a TagQuery.
074     * @param operator the operator.
075     * @param descendantAutoposting true to enable descendant autoposting (parent tags are found), false otherwise.
076     * @param value the tag id to test.
077     */
078    public TagQuery(Operator operator, boolean descendantAutoposting, String value)
079    {
080        this(operator, descendantAutoposting, new String[] {value});
081    }
082    
083    /**
084     * Build a TagQuery.
085     * @param operator the operator.
086     * @param descendantAutoposting true to enable descendant autoposting (parent tags are found), false otherwise.
087     * @param values the tag IDs to test.
088     */
089    public TagQuery(Operator operator, boolean descendantAutoposting, String... values)
090    {
091        this(operator, descendantAutoposting, LogicalOperator.OR, values);
092    }
093    
094    /**
095     * Build a TagQuery.
096     * @param operator the operator. The operator is applied to each value individually. 
097     * For instance, if operator is {@link Query.Operator#NE NE} and logicalOperator is
098     * {@link Query.LogicalOperator#OR OR}, then the Query represents: <code>(NOT A) OR (NOT B) ...</code>
099     * @param descendantAutoposting true to enable descendant autoposting (parent tags are found), false otherwise.
100     * @param logicalOperator the logical operator.
101     * @param values the tag IDs to test.
102     */
103    public TagQuery(Operator operator, boolean descendantAutoposting, LogicalOperator logicalOperator, String... values)
104    {
105        if (Operator.EQ != operator && Operator.NE != operator && Operator.EXISTS != operator)
106        {
107            throw new IllegalArgumentException("Test operator '" + operator + "' can't be used for a tag query.");
108        }
109        
110        _operator = operator;
111        _logicalOperator = logicalOperator;
112        _values = values;
113        _descendantAutoposting = descendantAutoposting;
114    }
115
116    /**
117     * Get the operator.
118     * @return the operator
119     */
120    public Operator getOperator()
121    {
122        return _operator;
123    }
124
125    /**
126     * Get the values.
127     * @return the values
128     */
129    public String[] getValues()
130    {
131        return _values;
132    }
133
134    /**
135     * Get the logicalOperator.
136     * @return the logicalOperator
137     */
138    public LogicalOperator getLogicalOperator()
139    {
140        return _logicalOperator;
141    }
142    
143    @Override
144    public String build() throws QuerySyntaxException
145    {
146        StringBuilder query = new StringBuilder();
147
148        if (_operator == Operator.EXISTS)
149        {
150            query.append(SolrFieldNames.ALL_TAGS).append(":").append(QueryHelper.EXISTS_VALUE);
151            
152            return query.toString();
153        }
154        
155        if (_values.length > 0)
156        {
157            for (int i = 0; i < _values.length; i++)
158            {
159                if (i > 0)
160                {
161                    query.append(' ').append(_logicalOperator).append(' ');
162                }
163                if (_values.length > 1)
164                {
165                    query.append('(');
166                }
167                
168                if (_operator == Operator.NE)
169                {
170                    NotQuery.appendNegation(query);
171                }
172                
173                query.append(_descendantAutoposting ? SolrFieldNames.ALL_TAGS : SolrFieldNames.TAGS)
174                    .append(':').append(_values[i]);
175                
176                if (_values.length > 1)
177                {
178                    query.append(')');
179                }
180            }
181        }
182        
183        return query.toString();
184    }
185
186    @Override
187    public int hashCode()
188    {
189        final int prime = 31;
190        int result = 1;
191        result = prime * result + (_descendantAutoposting ? 1231 : 1237);
192        result = prime * result + ((_logicalOperator == null) ? 0 : _logicalOperator.hashCode());
193        result = prime * result + ((_operator == null) ? 0 : _operator.hashCode());
194        result = prime * result + Arrays.hashCode(_values);
195        return result;
196    }
197
198    @Override
199    public boolean equals(Object obj)
200    {
201        if (this == obj)
202        {
203            return true;
204        }
205        if (obj == null)
206        {
207            return false;
208        }
209        if (getClass() != obj.getClass())
210        {
211            return false;
212        }
213        TagQuery other = (TagQuery) obj;
214        if (_descendantAutoposting != other._descendantAutoposting)
215        {
216            return false;
217        }
218        if (_logicalOperator != other._logicalOperator)
219        {
220            return false;
221        }
222        if (_operator != other._operator)
223        {
224            return false;
225        }
226        if (!Arrays.equals(_values, other._values))
227        {
228            return false;
229        }
230        return true;
231    }
232}