/*
* 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();
}