/* * Copyright (C) 2009 eXo Platform SAS. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.exoplatform.services.jcr.impl.core.itemfilters; import org.exoplatform.services.jcr.datamodel.ItemData; import org.exoplatform.services.jcr.datamodel.QPathEntry; import org.exoplatform.services.jcr.impl.core.JCRPath; import org.exoplatform.services.jcr.impl.core.SessionImpl; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.StringTokenizer; import java.util.regex.Pattern; import javax.jcr.RepositoryException; /** * Created by The eXo Platform SAS. * * @author <a href="mailto:geaz@users.sourceforge.net">Gennady Azarenkov</a> * @version $Id: AbstractNamePatternFilter.java 2137 2010-03-25 15:31:56Z sergiykarpenko $ */ public abstract class AbstractNamePatternFilter implements ItemDataFilter { /** * Logger. */ protected static final Log LOG = ExoLogger.getLogger("exo.jcr.component.core.ItemDataNamePatternFilter"); private final SessionImpl session; private final boolean getAllItems; private final List<QPathEntryFilter> subFilters; /** * Exact names. */ private final QPathEntry[] exactNames; /** * Not parsed names with wildcard symbols. */ private final String[] wildcardExpressions; /** * wildcardExpressions parsed into Pattern. */ private Pattern[] expressionNamePatterns = null; public AbstractNamePatternFilter(String namePattern, SessionImpl session) throws RepositoryException { this.session = session; StringTokenizer parser = new StringTokenizer(namePattern, "|"); boolean getAll = false; List<QPathEntryFilter> filters = new ArrayList<QPathEntryFilter>(); List<QPathEntry> exactNamesList = new ArrayList<QPathEntry>(); List<String> wildcardExpressionsList = new ArrayList<String>(); Set<String> existedTokens = new HashSet<String>(); while (parser.hasMoreTokens()) { String token = parser.nextToken().trim(); if (existedTokens.contains(token)) { continue; } existedTokens.add(token); if (token.equals("*") || token.equals("*:*") || token.equals("*:*[*]")) { getAll = true; filters.clear(); break; } if (token.startsWith(":")) { throw new RepositoryException("Name pattern can not start with colon."); } else { QPathEntry entry = null; if (isExactName(token)) { JCRPath path = session.getLocationFactory().parseRelPath(token); QPathEntry[] entries = path.getInternalPath().getEntries(); entry = entries[entries.length - 1]; exactNamesList.add(entry); filters.add(new ExactQPathEntryFilter(entry)); } else { entry = parsePatternQPathEntry(token, session); wildcardExpressionsList.add(token); filters.add(new PatternQPathEntryFilter(entry)); } } } getAllItems = getAll; if (getAllItems) { subFilters = null; exactNames = null; wildcardExpressions = null; } else { subFilters = filters; exactNames = new QPathEntry[exactNamesList.size()]; exactNamesList.toArray(exactNames); wildcardExpressions = new String[wildcardExpressionsList.size()]; wildcardExpressionsList.toArray(wildcardExpressions); } } /** * {@inheritDoc} */ public boolean accept(ItemData item) { if (getAllItems) { return true; } if (expressionNamePatterns == null) { initializePatterns(); } try { // check exact names for first QPathEntry itemEntry = item.getQPath().getEntries()[item.getQPath().getDepth()]; for (QPathEntry entry : exactNames) { if (entry.equals(itemEntry)) { return true; } } JCRPath.PathElement[] pathElements = session.getLocationFactory().createRelPath(new QPathEntry[]{itemEntry}); // prefix:name[index] String name = pathElements[0].getAsString(true); for (Pattern pattern : expressionNamePatterns) { if (pattern.matcher(name).matches()) { return true; } } } catch (RepositoryException e) { // if error - just log and don't accept it LOG.error("Cannot parse JCR name for " + item.getQPath().getAsString(), e); } return false; } /** * {@inheritDoc} */ public List<? extends ItemData> accept(List<? extends ItemData> itemData) { if (getAllItems) { return itemData; } if (expressionNamePatterns == null) { initializePatterns(); } List<ItemData> result = new ArrayList<ItemData>(); for (int i = 0; i < itemData.size(); i++) { if (accept(itemData.get(i))) { result.add(itemData.get(i)); } } return result; } /** * Check does pattern looking all data. * @return */ public boolean isLookingAllData() { return getAllItems; } /** * Get sub filters. Each Pattern name stored in own subfilter. * @return */ public List<QPathEntryFilter> getQPathEntryFilters() { return subFilters; } private void initializePatterns() { expressionNamePatterns = new Pattern[wildcardExpressions.length]; for (int i = 0; i < wildcardExpressions.length; i++) { StringBuilder sb = new StringBuilder(); for (char c : wildcardExpressions[i].toCharArray()) { switch (c) { case '*' : sb.append(".*"); break; case '\\' : case ':' : case '+' : case '-' : case '=' : case '.' : case ',' : case '{' : case '}' : case '(' : case ')' : case '[' : case ']' : case '^' : case '&' : case '$' : case '?' : case '<' : case '>' : case '!' : sb.append('\\'); default : sb.append(c); } } if (wildcardExpressions[i].indexOf('[') == -1) { sb.append("\\[.*\\]"); } expressionNamePatterns[i] = Pattern.compile(sb.toString()); } } /** * Parse QPathEntry from string namePattern. NamePattern may contain wildcard symbols in * namespace and local name. And may not contain index, which means look all samename siblings. * So ordinary QPathEntry parser is not acceptable. * * @param namePattern string pattern * @param session session used to fetch namespace URI * @return PatternQPathEntry * @throws RepositoryException if namePattern is malformed or there is some namespace problem. */ private QPathEntry parsePatternQPathEntry(String namePattern, SessionImpl session) throws RepositoryException { int colonIndex = namePattern.indexOf(':'); int bracketIndex = namePattern.lastIndexOf('['); String namespaceURI; String localName; int index = getDefaultIndex(); if (bracketIndex != -1) { int rbracketIndex = namePattern.lastIndexOf(']'); if (rbracketIndex < bracketIndex) { throw new RepositoryException("Malformed pattern expression " + namePattern); } index = Integer.parseInt(namePattern.substring(bracketIndex + 1, rbracketIndex)); } if (colonIndex == -1) { namespaceURI = ""; localName = (bracketIndex == -1) ? namePattern : namePattern.substring(0, bracketIndex); } else { String prefix = namePattern.substring(0, colonIndex); localName = (bracketIndex == -1) ? namePattern.substring(colonIndex + 1) : namePattern.substring(0, bracketIndex); if (prefix.indexOf("*") != -1) { namespaceURI = "*"; } else { namespaceURI = session.getNamespaceURI(prefix); } } return new PatternQPathEntry(namespaceURI, localName, index); } /** * Check is token exact name. * @param token * @return */ protected abstract boolean isExactName(String token); /** * Returns index that will be used, if pattern has no index. * @return index */ protected abstract int getDefaultIndex(); }