1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.module.templatingkit.search;
35
36 import info.magnolia.cms.core.Content;
37 import info.magnolia.cms.core.ItemType;
38 import info.magnolia.cms.core.NodeData;
39 import info.magnolia.cms.util.ContentUtil;
40 import info.magnolia.jcr.util.NodeTypes;
41 import info.magnolia.jcr.util.PropertyUtil;
42 import info.magnolia.module.templatingkit.templates.category.TemplateCategoryUtil;
43 import info.magnolia.templating.functions.TemplatingFunctions;
44
45 import java.util.ArrayList;
46 import java.util.Calendar;
47 import java.util.Collection;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.regex.Pattern;
51
52 import javax.jcr.Node;
53 import javax.jcr.PropertyType;
54 import javax.jcr.RepositoryException;
55
56 import org.apache.commons.lang.StringUtils;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59
60
61
62
63
64
65
66
67
68
69 public class SearchResultItem {
70 private static final Logger log = LoggerFactory.getLogger(SearchResultItem.class);
71
72 private static final Pattern HTML_STRIP = Pattern.compile("<.*?>", Pattern.DOTALL);
73
74
75
76
77 protected int chars = 100;
78
79
80
81
82 protected int maxSnippets = 3;
83
84 private Calendar date;
85 private String author;
86 private String category;
87 private String title;
88 private String text;
89 private Node node;
90 private String link;
91 protected String query;
92
93
94
95
96
97
98 public SearchResultItem(Node node, String query, TemplatingFunctions templatingFunctions) {
99 this.query = query;
100 this.node = node;
101 this.title = PropertyUtil.getString(node, "title");
102 this.author = PropertyUtil.getString(node, "author");
103
104 this.category = StringUtils.capitalize(TemplateCategoryUtil.getTemplateSubCategory(node));
105 if(StringUtils.isEmpty(category)){
106 this.category = StringUtils.capitalize(TemplateCategoryUtil.getTemplateCategory(node));
107 }
108
109 this.date = PropertyUtil.getDate(node, "date");
110 if(date == null){
111 try {
112 this.date = NodeTypes.LastModified.getLastModified(node);
113 } catch (RepositoryException e) {
114
115 }
116 }
117
118 this.link = templatingFunctions.link(node);
119 this.text = this.getSnippets();
120 }
121
122 public SearchResultItem(Node node, String query, String title, String author, String category, Calendar date, String link) {
123 this.query = query;
124 this.node = node;
125 this.title = title;
126 this.author = author;
127
128 this.category = category;
129 this.date = date;
130 this.link = link;
131
132 this.text = this.getSnippets();
133 }
134
135 public Calendar getDate() {
136 return date;
137 }
138
139 public String getAuthor() {
140 return author;
141 }
142
143 public String getCategory() {
144 return category;
145 }
146
147 public String getTitle() {
148 return title;
149 }
150
151 public String getText() {
152 return text;
153 }
154
155
156
157
158 public String getFormattedText() {
159 return text;
160 }
161
162 public String getLink() {
163 return link;
164 }
165
166 public Node getNode() {
167 return node;
168 }
169
170 public String getSnippets() {
171
172 log.debug("collecting snippets");
173
174 StringBuffer snippets = new StringBuffer();
175 int numberOfSnippets = 0;
176
177 boolean endProcess = false;
178 if(processNodeDatas(numberOfSnippets, snippets, ContentUtil.asContent(node))) {
179
180 Collection paragraphCollections = ContentUtil.asContent(node).getChildren(ItemType.CONTENTNODE);
181
182 Iterator iterator = paragraphCollections.iterator();
183 while (iterator.hasNext() && !endProcess) {
184 Content paragraphCollection = (Content) iterator.next();
185 if(!processNodeDatas(numberOfSnippets, snippets, paragraphCollection )) {
186 endProcess = true;
187 break;
188 }
189 Collection<Content> paragraphs = paragraphCollection.getChildren(ItemType.CONTENTNODE);
190
191 for (Content paragraph : paragraphs) {
192 if (!processNodeDatas(numberOfSnippets, snippets, paragraph)) {
193 endProcess = true;
194 break;
195 }
196
197 log.debug("Iterating on paragraph {}", paragraph);
198
199
200 }
201 }
202 }
203
204 return snippets.toString();
205 }
206
207
208 public int getMaxSnippets() {
209 return maxSnippets;
210 }
211
212 public void setMaxSnippets(int maxSnippets) {
213 this.maxSnippets = maxSnippets;
214 }
215
216
217
218
219
220
221 protected boolean processNodeDatas(int numberOfSnippets, StringBuffer snippets, Content paragraph) {
222
223 Collection<NodeData> properties = paragraph.getNodeDataCollection();
224 List<String> snippetList = new ArrayList<String>();
225
226 Iterator<NodeData> dataIterator = properties.iterator();
227 while (dataIterator.hasNext()) {
228 NodeData property = dataIterator.next();
229 if (property.getType() != PropertyType.BINARY) {
230 highlightSnippet(snippetList, property);
231 if (snippetList.size() + numberOfSnippets >= maxSnippets) {
232 break;
233 }
234 }
235 }
236 for (String snippet : snippetList) {
237 snippets.append(snippet);
238 }
239
240 return snippetList.size() + numberOfSnippets < maxSnippets;
241 }
242
243 protected void highlightSnippet(List<String> snippetList, NodeData property) {
244 String resultString = property.getString();
245
246 log.debug("Iterating on property {}", property.getName());
247 log.debug("Property value is {}", resultString);
248
249 String searchTerm = this.query;
250
251 if (skipProperty(property.getName(), resultString, searchTerm)) {
252 return;
253 }
254
255
256 if ( searchTerm != null && searchTerm.length() > 2) {
257
258 log.debug("Looking for search term [{}] in [{}]", searchTerm, resultString);
259
260
261 if (StringUtils.startsWith(searchTerm, "\"") && StringUtils.endsWith(searchTerm, "\"")) {
262 searchTerm = searchTerm.substring(1,searchTerm.length() - 1);
263 }
264
265
266
267 if (!StringUtils.contains(resultString.toLowerCase(), searchTerm.toLowerCase())) {
268 return;
269 }
270
271
272 resultString = stripHtmlTags(resultString);
273
274
275 int pos = resultString.toLowerCase().indexOf(searchTerm.toLowerCase());
276 if (pos > -1) {
277
278 int posEnd = pos + searchTerm.length();
279 int from = (pos - chars / 2);
280 if (from < 0) {
281 from = 0;
282 }
283
284 int to = from + chars;
285 if (to > resultString.length()) {
286 to = resultString.length();
287 }
288
289 StringBuffer snippet = new StringBuffer();
290
291 snippet.append(StringUtils.substring(resultString, from, pos));
292 snippet.append("<em class=\"highlight\">");
293 snippet.append(StringUtils.substring(resultString, pos, posEnd));
294 snippet.append("</em>");
295 snippet.append(StringUtils.substring(resultString, posEnd, to));
296
297 if (from > 0) {
298 snippet.insert(0, "... ");
299 }
300 if (to < resultString.length()) {
301 snippet.append("... ");
302 }
303
304 log.debug("Search term found, adding snippet {}", snippet);
305
306 snippetList.add(snippet + "<br />");
307 }
308 }
309 }
310
311 protected boolean skipProperty(String name, String resultString, String searchTerm) {
312
313
314 return resultString.length() < 20;
315 }
316
317 protected String stripHtmlTags(String resultString) {
318 return HTML_STRIP.matcher(resultString).replaceAll("");
319 }
320
321
322 }