001/* 002 * Copyright 2011 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.plugins.glossary; 017 018import org.apache.commons.lang.StringUtils; 019 020import org.ametys.plugins.repository.AmetysRepositoryException; 021import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 022import org.ametys.plugins.repository.RepositoryConstants; 023import org.ametys.plugins.repository.query.SortCriteria; 024import org.ametys.plugins.repository.query.expression.Expression; 025import org.ametys.web.repository.site.Site; 026 027/** 028 * Glossary helper. 029 */ 030public final class GlossaryHelper 031{ 032 033 /** The glossary page tag. */ 034 public static final String GLOSSARY_PAGE_TAG = "GLOSSARY"; 035 036 private static final String __PLUGIN_NODE_NAME = "glossary"; 037 038 private static final String __DEFINITIONS_NODE_NAME = "ametys:wordDefinitions"; 039 040 private GlossaryHelper() 041 { 042 // Hides the default constructor. 043 } 044 045 /** 046 * Get the root plugin storage object. 047 * @param site the site. 048 * @return the root plugin storage object. 049 * @throws AmetysRepositoryException if a repository error occurs. 050 */ 051 public static ModifiableTraversableAmetysObject getPluginNode(Site site) throws AmetysRepositoryException 052 { 053 try 054 { 055 ModifiableTraversableAmetysObject pluginsNode = site.getRootPlugins(); 056 057 return getOrCreateNode(pluginsNode, __PLUGIN_NODE_NAME, "ametys:unstructured"); 058 } 059 catch (AmetysRepositoryException e) 060 { 061 throw new AmetysRepositoryException("Error getting the glossary plugin node for site " + site.getName(), e); 062 } 063 } 064 065 /** 066 * Get the word definitions storage node. 067 * @param site the site 068 * @param language the language. 069 * @return the word definitions storage node. 070 * @throws AmetysRepositoryException if a repository error occurs. 071 */ 072 public static ModifiableTraversableAmetysObject getDefinitionsNode(Site site, String language) throws AmetysRepositoryException 073 { 074 try 075 { 076 // Get the root plugin node. 077 ModifiableTraversableAmetysObject pluginNode = getPluginNode(site); 078 079 // Get or create the language node. 080 ModifiableTraversableAmetysObject langNode = getOrCreateNode(pluginNode, language, "ametys:unstructured"); 081 082 // Get or create the definitions container node in the language node and return it. 083 return getOrCreateNode(langNode, __DEFINITIONS_NODE_NAME, DefaultDefinitionFactory.DEFINITION_ROOT_NODE_TYPE); 084 } 085 catch (AmetysRepositoryException e) 086 { 087 throw new AmetysRepositoryException("Error getting the word definitions node for site " + site.getName() + " and language " + language, e); 088 } 089 } 090 091 /** 092 * Get the word definitions storage node. 093 * @param siteName the site name. 094 * @return the word definitions storage node. 095 */ 096 public static String getPluginNodePath(String siteName) 097 { 098 return String.format("//element(%s, ametys:site)/ametys-internal:plugins/%s", siteName, __PLUGIN_NODE_NAME); 099 } 100 101 /** 102 * Get the word definitions storage node. 103 * @param siteName the site name. 104 * @param language the language. 105 * @return the word definitions storage node. 106 */ 107 public static String getDefinitionsNodePath(String siteName, String language) 108 { 109 return getPluginNodePath(siteName) + "/" + language + "/" + __DEFINITIONS_NODE_NAME; 110 } 111 112 /** 113 * Creates the XPath query for glossary words corresponding to specified {@link Expression} and {@link SortCriteria}. 114 * @param siteName the site name. 115 * @param language the language 116 * @param expression the query predicates. Can be null. 117 * @return the created XPath query 118 */ 119 public static String getDefinitionQuery(String siteName, String language, Expression expression) 120 { 121 SortCriteria sortCriteria = new SortCriteria(); 122 sortCriteria.addJCRPropertyCriterion(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":word", true, true); 123 124 return getDefinitionQuery(siteName, language, expression, sortCriteria); 125 } 126 127 /** 128 * Creates the XPath query for glossary words corresponding to specified {@link Expression} and {@link SortCriteria}. 129 * @param siteName the site name. 130 * @param language the language 131 * @param expression the query predicates. Can be null. 132 * @param sortCriteria criteria for sorting results. Can be null. 133 * @return the created XPath query 134 */ 135 public static String getDefinitionQuery(String siteName, String language, Expression expression, SortCriteria sortCriteria) 136 { 137 String predicats = null; 138 139 if (expression != null) 140 { 141 predicats = StringUtils.trimToNull(expression.build()); 142 } 143 144 String xpathQuery = getDefinitionsNodePath(siteName, language) + "/element(*, " + DefaultDefinitionFactory.DEFINITION_NODE_TYPE + ")" + (predicats != null ? "[" + predicats + "]" : "") + ((sortCriteria != null) ? (" " + sortCriteria.build()) : ""); 145 return xpathQuery; 146 } 147 148 /** 149 * Get the word definitions storage node. 150 * @param siteName the site name. 151 * @param language the language. 152 * @param word the searched word 153 * @return the word definitions storage node. 154 */ 155 public static String getWordExistsQuery(String siteName, String language, String word) 156 { 157 String lowerWord = StringUtils.replace(word, "'", "''").toLowerCase(); 158 return getDefinitionsNodePath(siteName, language) + "/element(*, " + DefaultDefinitionFactory.DEFINITION_NODE_TYPE 159 + ")[fn:lower-case(@ametys-internal:word) = '" + lowerWord + "' or fn:lower-case(@ametys-internal:variants) = '" + lowerWord + "']"; 160 } 161 162 /** 163 * Get the word definitions storage node. 164 * @param siteName the site name. 165 * @param language the language. 166 * @param letter the first letter. 167 * @return the word definitions storage node. 168 */ 169 public static String getFirstLetterDefQuery(String siteName, String language, String letter) 170 { 171 String lowerLetter = letter.toLowerCase(); 172 return getDefinitionsNodePath(siteName, language) + "/element(*, " + DefaultDefinitionFactory.DEFINITION_NODE_TYPE 173 + ")[jcr:like(fn:lower-case(@ametys-internal:word), '" + lowerLetter + "%')] order by @ametys-internal:word"; 174 } 175 176 /** 177 * Get the word definitions storage node when first letter is not an alpha. 178 * @param siteName the site name. 179 * @param language the language. 180 * @return the word definitions storage node. 181 */ 182 public static String getNonAlphaFirstLetterDefQuery(String siteName, String language) 183 { 184 return getDefinitionsNodePath(siteName, language) + "/element(*, " + DefaultDefinitionFactory.DEFINITION_NODE_TYPE 185 + ")[not(jcr:like(fn:lower-case(@ametys-internal:word), 'a%')) " 186 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'b%'))" 187 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'c%'))" 188 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'd%'))" 189 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'e%'))" 190 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'f%'))" 191 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'g%'))" 192 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'h%'))" 193 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'u%'))" 194 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'j%'))" 195 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'k%'))" 196 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'l%'))" 197 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'm%'))" 198 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'n%'))" 199 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'o%'))" 200 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'p%'))" 201 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'q%'))" 202 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'r%'))" 203 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 's%'))" 204 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 't%'))" 205 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'u%'))" 206 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'v%'))" 207 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'w%'))" 208 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'x%'))" 209 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'y%'))" 210 + "and not(jcr:like(fn:lower-case(@ametys-internal:word), 'z%'))" 211 + "] order by @ametys-internal:word"; 212 } 213 214 private static ModifiableTraversableAmetysObject getOrCreateNode(ModifiableTraversableAmetysObject parentNode, String nodeName, String nodeType) throws AmetysRepositoryException 215 { 216 ModifiableTraversableAmetysObject definitionsNode; 217 if (parentNode.hasChild(nodeName)) 218 { 219 definitionsNode = parentNode.getChild(nodeName); 220 } 221 else 222 { 223 definitionsNode = parentNode.createChild(nodeName, nodeType); 224 parentNode.saveChanges(); 225 } 226 return definitionsNode; 227 } 228 229}