/*=============================================================================*
* Copyright 2004 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*=============================================================================*/
package org.apache.ws.notification.topics.expression.impl;
import org.apache.ws.notification.topics.Topic;
import org.apache.ws.notification.topics.TopicSet;
import org.apache.ws.notification.topics.TopicSpace;
import org.apache.ws.notification.topics.TopicSpaceSet;
import org.apache.ws.notification.topics.expression.InvalidTopicExpressionException;
import org.apache.ws.notification.topics.expression.TopicExpression;
import org.apache.ws.notification.topics.expression.TopicExpressionEvaluator;
import org.apache.ws.notification.topics.expression.TopicExpressionException;
import org.apache.ws.notification.topics.expression.TopicExpressionResolutionException;
import org.apache.ws.notification.topics.expression.TopicPathDialectUnknownException;
import org.apache.ws.notification.topics.expression.impl.AbstractTopicExpressionEvaluator;
import org.apache.ws.notification.topics.v2004_06.TopicsConstants;
import org.apache.ws.util.xml.NamespaceContext;
import javax.xml.namespace.QName;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.StringTokenizer;
/**
* Topic expression evalutor for the WS-Topics "Full" topic dialect.
*
* @see TopicExpressionEvaluator
*
* @author Ian Springer (ian DOT springer AT hp DOT com)
*/
public class FullTopicExpressionEvaluator
extends AbstractTopicExpressionEvaluator
{
//private static final Log LOG = LogFactory.getLog( FullTopicExpressionEvaluator.class.getName() );
private static final String[] SUPPORTED_DIALECTS =
{
TopicsConstants.TOPIC_EXPR_DIALECT_FULL
};
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public String[] getDialects( )
{
return SUPPORTED_DIALECTS;
}
/**
* DOCUMENT_ME
*
* @param topicSpaceSet DOCUMENT_ME
* @param topicExpr DOCUMENT_ME
*
* @return DOCUMENT_ME
*
* @throws TopicPathDialectUnknownException DOCUMENT_ME
* @throws TopicExpressionResolutionException DOCUMENT_ME
* @throws InvalidTopicExpressionException DOCUMENT_ME
* @throws TopicExpressionException DOCUMENT_ME
*/
public Topic[] evaluate( TopicSpaceSet topicSpaceSet,
TopicExpression topicExpr )
throws TopicPathDialectUnknownException,
TopicExpressionResolutionException,
InvalidTopicExpressionException,
TopicExpressionException
{
String expr = getContent( topicExpr );
NamespaceContext nsContext = getNamespaceContext( topicExpr );
StringTokenizer exprTokenizer = new StringTokenizer( expr, "|" );
Set allMatchedTopics = new HashSet( );
while ( exprTokenizer.hasMoreTokens( ) )
{
QName topicPath = toQName( exprTokenizer.nextToken( ),
nsContext );
List matchedTopics = evaluateTopicPath( topicSpaceSet, topicPath );
allMatchedTopics.addAll( matchedTopics );
}
if ( topicSpaceSet.isFixed( ) && allMatchedTopics.isEmpty( ) )
{
throw new InvalidTopicExpressionException( "Full topic expression '" + expr
+ "' does not match any topics, and the target topic set is fixed." );
}
return (Topic[]) allMatchedTopics.toArray( new Topic[0] );
}
private List evaluateTopicPath( TopicSpaceSet topicSpaceSet,
QName topicPath )
throws TopicExpressionResolutionException,
InvalidTopicExpressionException
{
List matchedTopics = new ArrayList( );
TopicSpace topicSpace = getTopicSpace( topicSpaceSet, topicPath );
if ( topicPath.getLocalPart( ).indexOf( "///" ) != -1 )
{
throw new InvalidTopicExpressionException( "Topic path '" + topicPath
+ "' contains an empty path component." );
}
PathTokenizer pathTokenizer = new PathTokenizer( topicPath.getLocalPart( ) );
List topicSetsToSearch = new ArrayList( );
topicSetsToSearch.add( topicSpace );
boolean atFirstToken = true;
while ( pathTokenizer.hasMoreTokens( ) )
{
String pathToken = pathTokenizer.nextToken( );
matchedTopics.clear( );
for ( int i = 0; i < topicSetsToSearch.size( ); i++ )
{
TopicSet topicSetToSearch = (TopicSet) topicSetsToSearch.get( i );
boolean recurse = pathToken.startsWith( "/" );
String name = recurse ? pathToken.substring( 1 ) : pathToken;
matchedTopics.addAll( findTopics( topicSetToSearch, name, recurse ) );
}
if ( atFirstToken && matchedTopics.isEmpty( ) )
{
throw new InvalidTopicExpressionException( "Topic path '" + topicPath
+ "' refers to a root topic that is not defined in the referenced topic space." );
}
topicSetsToSearch.clear( );
topicSetsToSearch.addAll( matchedTopics );
atFirstToken = false;
}
return matchedTopics;
}
private List findTopics( TopicSet topicSet,
String name )
{
List matchedTopics = new ArrayList( );
if ( name.equals( "*" ) )
{
Iterator topicIter = topicSet.topicIterator( );
while ( topicIter.hasNext( ) )
{
matchedTopics.add( (Topic) topicIter.next( ) );
}
}
else
{
if ( topicSet.containsTopic( name ) )
{
matchedTopics.add( topicSet.getTopic( name ) );
}
}
return matchedTopics;
}
private List findTopics( TopicSet topicSet,
String name,
boolean recurse )
throws InvalidTopicExpressionException
{
List allMatchedTopics = new ArrayList( );
if ( name.equals( "." ) )
{
allMatchedTopics.add( topicSet );
name = "*"; // we only want to evaluate "." the first time through during recursion
}
List matchedTopics = findTopics( topicSet, name );
allMatchedTopics.addAll( matchedTopics );
if ( recurse )
{
Iterator topicIter = topicSet.topicIterator( );
while ( topicIter.hasNext( ) )
{
allMatchedTopics.addAll( findTopics( (Topic) topicIter.next( ), name, recurse ) );
}
}
return allMatchedTopics;
}
/**
* DOCUMENT_ME
*
* @version $Revision: 1.8 $
* @author $author$
*/
protected class PathTokenizer
{
private String m_path;
private int m_currentPos;
private int m_maxPos;
/**
* Creates a new {@link PathTokenizer} object.
*
* @param path DOCUMENT_ME
*/
public PathTokenizer( String path )
{
m_path = path;
m_maxPos = m_path.length( );
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*/
public boolean hasMoreTokens( )
{
return m_currentPos < m_maxPos;
}
/**
* DOCUMENT_ME
*
* @return DOCUMENT_ME
*
* @throws InvalidTopicExpressionException DOCUMENT_ME
* @throws NoSuchElementException DOCUMENT_ME
*/
public String nextToken( )
throws InvalidTopicExpressionException
{
if ( m_currentPos >= m_maxPos )
{
throw new NoSuchElementException( );
}
int startPos = m_currentPos;
m_currentPos = scanToken( startPos );
String token = m_path.substring( startPos, m_currentPos );
if ( token.startsWith( "//" ) )
{
token = token.substring( 1 );
}
++m_currentPos; // skip the slash
return token;
}
private int scanToken( int startPos )
throws InvalidTopicExpressionException
{
int newPos = startPos;
if ( newPos == 0 ) // special handling for head of path, as per spec
{
if ( m_path.startsWith( "//" ) )
{
newPos += 2;
}
if ( m_path.charAt( newPos ) == '.' )
{
throw new InvalidTopicExpressionException( "'.' may not be used as the first component of a topic path." );
}
}
else
{
if ( m_path.charAt( newPos ) == '/' )
{
newPos += 1; // do not count leading slash as a delim
}
}
while ( newPos < m_maxPos )
{
if ( m_path.charAt( newPos ) == '/' )
{
break;
}
newPos++;
}
return newPos;
}
}
}