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