1. Project Clover database Tue Dec 20 2016 21:24:09 CET
  2. Package org.xwiki.query.internal

File UniqueDocumentFilter.java

 

Coverage histogram

../../../../img/srcFileCovDistChart10.png
0% of files have more coverage

Code metrics

16
38
3
1
142
80
14
0.37
12.67
3
4.67

Classes

Class Line # Actions
UniqueDocumentFilter 44 38 0% 14 0
1.0100%
 

Contributing tests

This file is covered by 6 tests. .

Source view

1    /*
2    * See the NOTICE file distributed with this work for additional
3    * information regarding copyright ownership.
4    *
5    * This is free software; you can redistribute it and/or modify it
6    * under the terms of the GNU Lesser General Public License as
7    * published by the Free Software Foundation; either version 2.1 of
8    * the License, or (at your option) any later version.
9    *
10    * This software is distributed in the hope that it will be useful,
11    * but WITHOUT ANY WARRANTY; without even the implied warranty of
12    * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13    * Lesser General Public License for more details.
14    *
15    * You should have received a copy of the GNU Lesser General Public
16    * License along with this software; if not, write to the Free
17    * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
18    * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
19    */
20    package org.xwiki.query.internal;
21   
22    import java.util.ArrayList;
23    import java.util.List;
24   
25    import javax.inject.Inject;
26    import javax.inject.Named;
27   
28    import org.slf4j.Logger;
29    import org.xwiki.component.annotation.Component;
30    import org.xwiki.component.annotation.InstantiationStrategy;
31    import org.xwiki.component.descriptor.ComponentInstantiationStrategy;
32    import org.xwiki.query.Query;
33   
34    /**
35    * Query filter making sure unique results are retrieved by a {@link org.xwiki.query.Query}. This transformation only
36    * works on queries selecting full names of XWikiDocuments.
37    *
38    * @version $Id: e5cf81af5d5f8ca4fb23d35a647a735b0af3bafe $
39    * @since 4.1M1
40    */
41    @Component
42    @Named(UniqueDocumentFilter.HINT)
43    @InstantiationStrategy(ComponentInstantiationStrategy.PER_LOOKUP)
 
44    public class UniqueDocumentFilter extends AbstractQueryFilter
45    {
46    /**
47    * The role hint of that component.
48    */
49    public static final String HINT = "unique";
50   
51    /**
52    * Used to log debug information.
53    */
54    @Inject
55    private Logger logger;
56   
57    /**
58    * Used to mark columns positions of columns that we add to the select clause in order to support Group By.
59    * Indeed in HQL, if you use Group by combined with "distinct" you need to have the group by element present in
60    * the select clause. We use this list when we filter out results in order to remove extra data added by these
61    * extra columns since they shouldn't be returned to the user.
62    */
63    private List<Integer> columnsToRemove = new ArrayList<Integer>();
64   
65    /**
66    * @param statement statement to filter.
67    * @return true if the filter can be applied to the passed statement, false otherwise.
68    */
 
69  263 toggle private boolean isFilterable(String statement)
70    {
71  263 return getSelectColumns(statement).contains(FULLNAME_COLUMN)
72    && !getSelectColumns(statement).contains("distinct doc.fullName");
73    }
74   
 
75  263 toggle @Override
76    public String filterStatement(String statement, String language)
77    {
78  263 StringBuilder builder = new StringBuilder();
79  263 String result = statement;
80  263 String original = statement;
81   
82  263 if (Query.HQL.equals(language) && isFilterable(statement)) {
83  261 String prettySeparator = ", ";
84  261 builder.append("select distinct doc.fullName");
85   
86    // Put back original select columns.
87  261 int columnPosition = 1;
88  261 List<String> selectColumns = getSelectColumns(statement);
89  261 for (String column : selectColumns) {
90  262 if (!FULLNAME_COLUMN.equals(column)) {
91  1 builder.append(prettySeparator);
92  1 builder.append(column);
93  1 columnPosition++;
94    }
95    }
96    // Put the order by columns in the select clause to circumvent HQL limitations (distinct+order by).
97  261 for (String column : getOrderByColumns(statement)) {
98  207 if (!FULLNAME_COLUMN.equals(column) && !selectColumns.contains(column)) {
99  206 builder.append(prettySeparator);
100  206 builder.append(column);
101    // Mark these columns as special so that we can remove them later on in #filterResults
102  206 this.columnsToRemove.add(columnPosition);
103  206 columnPosition++;
104    }
105    }
106  261 builder.append(" ");
107  261 builder.append(statement.substring(statement.indexOf(" from ")).trim());
108  261 result = builder.toString();
109    }
110   
111  263 if (!statement.equals(result)) {
112  261 logger.debug("Query [{}] has been transformed into [{}]", original, result);
113    }
114   
115  263 return result;
116    }
117   
 
118  260 toggle @Override
119    public List filterResults(List results)
120    {
121    // If we had to put multiple columns in the select we need to remove them.
122  260 if (results.size() > 0 && results.get(0).getClass().isArray()) {
123  190 List filteredResults = new ArrayList();
124  190 for (Object result : results) {
125  610 Object[] actualResult = (Object[]) result;
126  610 Object[] newResult = new Object[actualResult.length - this.columnsToRemove.size()];
127  610 int j = 0;
128  1831 for (int i = 0; i < actualResult.length; i++) {
129  1221 if (!this.columnsToRemove.contains(i)) {
130  612 newResult[j++] = actualResult[i];
131    }
132    }
133    // Return the column value when there is only one column.
134  610 filteredResults.add(newResult.length == 1 ? newResult[0] : newResult);
135    }
136   
137  190 return filteredResults;
138    }
139   
140  70 return results;
141    }
142    }