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}