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}