1 | /* |
---|
2 | * Copyright (C) 2014 CLARIN |
---|
3 | * |
---|
4 | * This program is free software: you can redistribute it and/or modify |
---|
5 | * it under the terms of the GNU General Public License as published by |
---|
6 | * the Free Software Foundation, either version 3 of the License, or |
---|
7 | * (at your option) any later version. |
---|
8 | * |
---|
9 | * This program is distributed in the hope that it will be useful, |
---|
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
12 | * GNU General Public License for more details. |
---|
13 | * |
---|
14 | * You should have received a copy of the GNU General Public License |
---|
15 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
---|
16 | */ |
---|
17 | package eu.clarin.cmdi.vlo.wicket.panels; |
---|
18 | |
---|
19 | import eu.clarin.cmdi.vlo.FacetConstants; |
---|
20 | import eu.clarin.cmdi.vlo.pojo.FacetSelection; |
---|
21 | import eu.clarin.cmdi.vlo.pojo.QueryFacetsSelection; |
---|
22 | import eu.clarin.cmdi.vlo.service.PageParametersConverter; |
---|
23 | import eu.clarin.cmdi.vlo.wicket.model.SolrFieldNameModel; |
---|
24 | import eu.clarin.cmdi.vlo.wicket.pages.FacetedSearchPage; |
---|
25 | import eu.clarin.cmdi.vlo.wicket.provider.FacetSelectionProvider; |
---|
26 | import eu.clarin.cmdi.vlo.wicket.provider.FieldValueConverterProvider; |
---|
27 | import java.util.Collection; |
---|
28 | import java.util.Iterator; |
---|
29 | import java.util.Locale; |
---|
30 | import java.util.Map; |
---|
31 | import org.apache.wicket.AttributeModifier; |
---|
32 | import org.apache.wicket.ajax.AjaxRequestTarget; |
---|
33 | import org.apache.wicket.ajax.markup.html.AjaxFallbackLink; |
---|
34 | import org.apache.wicket.markup.html.WebMarkupContainer; |
---|
35 | import org.apache.wicket.markup.html.basic.Label; |
---|
36 | import org.apache.wicket.markup.html.link.BookmarkablePageLink; |
---|
37 | import org.apache.wicket.markup.html.link.Link; |
---|
38 | import org.apache.wicket.markup.html.panel.GenericPanel; |
---|
39 | import org.apache.wicket.markup.repeater.Item; |
---|
40 | import org.apache.wicket.markup.repeater.data.DataView; |
---|
41 | import org.apache.wicket.model.IModel; |
---|
42 | import org.apache.wicket.model.PropertyModel; |
---|
43 | import org.apache.wicket.spring.injection.annot.SpringBean; |
---|
44 | import org.apache.wicket.util.convert.ConversionException; |
---|
45 | import org.apache.wicket.util.convert.IConverter; |
---|
46 | |
---|
47 | /** |
---|
48 | * A panel representing the action trail that has lead to the current page in |
---|
49 | * its current state with links to revert to a previous state (not completely |
---|
50 | * linear given the nature of faceted browsing) |
---|
51 | * |
---|
52 | * @author twagoo |
---|
53 | */ |
---|
54 | public class BreadCrumbPanel extends GenericPanel<QueryFacetsSelection> { |
---|
55 | |
---|
56 | @SpringBean(name = "queryParametersConverter") |
---|
57 | private PageParametersConverter<QueryFacetsSelection> paramsConverter; |
---|
58 | @SpringBean |
---|
59 | private FieldValueConverterProvider fieldValueConverterProvider; |
---|
60 | |
---|
61 | private final WebMarkupContainer query; |
---|
62 | private final WebMarkupContainer facets; |
---|
63 | |
---|
64 | public BreadCrumbPanel(String id, IModel<QueryFacetsSelection> model) { |
---|
65 | super(id, model); |
---|
66 | add(new BookmarkablePageLink("searchPage", FacetedSearchPage.class)); |
---|
67 | add(query = createQuery(model, "query")); |
---|
68 | add(facets = createFacets(model, "facets")); |
---|
69 | } |
---|
70 | |
---|
71 | private WebMarkupContainer createQuery(final IModel<QueryFacetsSelection> selectionModel, String id) { |
---|
72 | final WebMarkupContainer queryContainer = new WebMarkupContainer(id); |
---|
73 | final Link link = new AjaxFallbackLink("leavequery") { |
---|
74 | |
---|
75 | @Override |
---|
76 | public void onClick(AjaxRequestTarget target) { |
---|
77 | // make query object without selection |
---|
78 | final QueryFacetsSelection newSelection = new QueryFacetsSelection(selectionModel.getObject().getQuery(), null); |
---|
79 | onSelectionChanged(newSelection, target); |
---|
80 | } |
---|
81 | }; |
---|
82 | link.add(new Label("content", new PropertyModel(selectionModel, "query"))); |
---|
83 | queryContainer.add(link); |
---|
84 | return queryContainer; |
---|
85 | } |
---|
86 | |
---|
87 | private WebMarkupContainer createFacets(final IModel<QueryFacetsSelection> model, String id) { |
---|
88 | final WebMarkupContainer facetsContainer = new WebMarkupContainer(id); |
---|
89 | facetsContainer.add(new AjaxFallbackLink("leaveselection") { |
---|
90 | |
---|
91 | @Override |
---|
92 | public void onClick(AjaxRequestTarget target) { |
---|
93 | onSelectionChanged(model.getObject(), target); |
---|
94 | } |
---|
95 | }); |
---|
96 | |
---|
97 | // create a provider that lists the facet name -> values entries |
---|
98 | final FacetSelectionProvider facetSelectionProvider = new FacetSelectionProvider(model); |
---|
99 | facetsContainer.add(new DataView<Map.Entry<String, FacetSelection>>("facet", facetSelectionProvider) { |
---|
100 | |
---|
101 | @Override |
---|
102 | protected void populateItem(final Item<Map.Entry<String, FacetSelection>> item) { |
---|
103 | final IModel<Map.Entry<String, FacetSelection>> selectionModel = item.getModel(); |
---|
104 | // add a label for the selected facet value(s) |
---|
105 | final Label valueLabel = new Label("value", new PropertyModel(selectionModel, "value")) { |
---|
106 | |
---|
107 | @Override |
---|
108 | public <C> IConverter<C> getConverter(Class<C> type) { |
---|
109 | final String facet = item.getModelObject().getKey(); |
---|
110 | // converter to render the value(s) nicely |
---|
111 | return (IConverter<C>) new SelectionConverter(facet, fieldValueConverterProvider.getConverter(facet)); |
---|
112 | } |
---|
113 | |
---|
114 | }; |
---|
115 | // add facet name as title attribute so that it becomes available through a tooltip |
---|
116 | valueLabel.add(new AttributeModifier("title", |
---|
117 | new SolrFieldNameModel(new PropertyModel(selectionModel, "key")))); |
---|
118 | item.add(valueLabel); |
---|
119 | |
---|
120 | // add a link for removal of the facet value selection |
---|
121 | item.add(new AjaxFallbackLink("removal") { |
---|
122 | |
---|
123 | @Override |
---|
124 | public void onClick(AjaxRequestTarget target) { |
---|
125 | // get a copy of the current selection |
---|
126 | final QueryFacetsSelection newSelection = model.getObject().getCopy(); |
---|
127 | final String facet = selectionModel.getObject().getKey(); |
---|
128 | // unselect this facet |
---|
129 | newSelection.selectValues(facet, null); |
---|
130 | onSelectionChanged(newSelection, target); |
---|
131 | } |
---|
132 | }); |
---|
133 | } |
---|
134 | }); |
---|
135 | |
---|
136 | return facetsContainer; |
---|
137 | } |
---|
138 | |
---|
139 | /** |
---|
140 | * Gets called if one of the links is clicked and the selection is changed. |
---|
141 | * This implementation sets the response page to {@link FacetedSearchPage} |
---|
142 | * with the new selection as its parameters |
---|
143 | * |
---|
144 | * @param selection new selection |
---|
145 | * @param target AJAX target, may be null |
---|
146 | */ |
---|
147 | protected void onSelectionChanged(QueryFacetsSelection selection, AjaxRequestTarget target) { |
---|
148 | setResponsePage(FacetedSearchPage.class, paramsConverter.toParameters(selection)); |
---|
149 | } |
---|
150 | |
---|
151 | @Override |
---|
152 | protected void onConfigure() { |
---|
153 | super.onConfigure(); |
---|
154 | |
---|
155 | final String queryString = getModelObject().getQuery(); |
---|
156 | final Map<String, FacetSelection> selection = getModelObject().getSelection(); |
---|
157 | |
---|
158 | query.setVisible(queryString != null && !queryString.isEmpty()); |
---|
159 | facets.setVisible(selection != null && !selection.isEmpty()); |
---|
160 | } |
---|
161 | |
---|
162 | /** |
---|
163 | * Converter for string collections, rendering depends on items in |
---|
164 | * collection (if singleton, show its value; if multiple, comma separated) |
---|
165 | */ |
---|
166 | private class SelectionConverter implements IConverter<FacetSelection> { |
---|
167 | |
---|
168 | private final String facet; |
---|
169 | private final IConverter<String> valueConverter; |
---|
170 | |
---|
171 | public SelectionConverter(String facet, IConverter<String> valueConverter) { |
---|
172 | this.facet = facet; |
---|
173 | this.valueConverter = valueConverter; |
---|
174 | } |
---|
175 | |
---|
176 | @Override |
---|
177 | public FacetSelection convertToObject(String value, Locale locale) throws ConversionException { |
---|
178 | throw new UnsupportedOperationException("Not supported yet."); |
---|
179 | } |
---|
180 | |
---|
181 | @Override |
---|
182 | public String convertToString(FacetSelection selection, Locale locale) { |
---|
183 | switch (selection.getSelectionType()) { |
---|
184 | case AND: |
---|
185 | return getCollectionString(selection, " and ", locale); |
---|
186 | case OR: |
---|
187 | return getCollectionString(selection, " or ", locale); |
---|
188 | case NOT: |
---|
189 | return "not [" + getCollectionString(selection, " or ", locale) + "]"; |
---|
190 | case NOT_EMPTY: |
---|
191 | return getAnyValueString(); |
---|
192 | default: |
---|
193 | return facet; |
---|
194 | } |
---|
195 | |
---|
196 | } |
---|
197 | |
---|
198 | private String getAnyValueString() { |
---|
199 | if (FacetConstants.FIELD_SEARCH_SERVICE.equals(facet)) { |
---|
200 | return "Content searchable"; |
---|
201 | } |
---|
202 | return "any " + facet; |
---|
203 | } |
---|
204 | |
---|
205 | public String getCollectionString(FacetSelection selection, String valueSeparator, Locale locale) { |
---|
206 | final Collection<String> value = selection.getValues(); |
---|
207 | //TODO: include selection type |
---|
208 | if (value.isEmpty()) { |
---|
209 | return ""; |
---|
210 | } else if (value.size() == 1) { |
---|
211 | return getConvertedValue(value.iterator().next(), locale); |
---|
212 | } else { |
---|
213 | final Iterator<String> iterator = value.iterator(); |
---|
214 | final StringBuilder sb = new StringBuilder(getConvertedValue(iterator.next(), locale)); |
---|
215 | while (iterator.hasNext()) { |
---|
216 | sb.append(valueSeparator).append(getConvertedValue(iterator.next(), locale)); |
---|
217 | } |
---|
218 | return sb.toString(); |
---|
219 | } |
---|
220 | } |
---|
221 | |
---|
222 | private String getConvertedValue(String string, Locale locale) { |
---|
223 | if (valueConverter != null) { |
---|
224 | final String converted = valueConverter.convertToString(string, locale); |
---|
225 | if (converted != null) { |
---|
226 | return converted; |
---|
227 | } |
---|
228 | } |
---|
229 | return string; |
---|
230 | } |
---|
231 | |
---|
232 | }; |
---|
233 | } |
---|