public class JmsConfiguration implements Cloneable {

    public static final String QUEUE_PREFIX = "queue:";
    public static final String TOPIC_PREFIX = "topic:";
    public static final String TEMP_QUEUE_PREFIX = "temp:queue:";
    public static final String TEMP_TOPIC_PREFIX = "temp:topic:";

    protected static final String TRANSACTED = "TRANSACTED";
    protected static final String CLIENT_ACKNOWLEDGE = "CLIENT_ACKNOWLEDGE";
    protected static final String AUTO_ACKNOWLEDGE = "AUTO_ACKNOWLEDGE";
    protected static final String DUPS_OK_ACKNOWLEDGE = "DUPS_OK_ACKNOWLEDGE";
    protected static final String REPLYTO_TEMP_DEST_AFFINITY_PER_COMPONENT = "component";
    protected static final String REPLYTO_TEMP_DEST_AFFINITY_PER_ENDPOINT = "endpoint";
    protected static final String REPLYTO_TEMP_DEST_AFFINITY_PER_PRODUCER = "producer";

    private static final transient Log LOG = LogFactory.getLog(JmsConfiguration.class);
    private JmsOperations jmsOperations;
    private DestinationResolver destinationResolver;
    private ConnectionFactory connectionFactory;
    private ConnectionFactory templateConnectionFactory;
    private ConnectionFactory listenerConnectionFactory;
    private int acknowledgementMode = -1;
    private String acknowledgementModeName;
    private ExceptionListener exceptionListener;
    private ConsumerType consumerType = ConsumerType.Default;
    private boolean autoStartup = true;
    private boolean acceptMessagesWhileStopping;
    private String clientId;
    private String durableSubscriptionName;
    private boolean subscriptionDurable;
    private boolean exposeListenerSession = true;
    private TaskExecutor taskExecutor;
    private boolean pubSubNoLocal;
    private int concurrentConsumers = 1;
    private int maxMessagesPerTask = 1;
    private ServerSessionFactory serverSessionFactory;
    private int cacheLevel = -1;
    private String cacheLevelName;
    private long recoveryInterval = -1;
    private long receiveTimeout = -1;
    private long requestTimeout = 20000L;
    private int idleTaskExecutionLimit = 1;
    private int maxConcurrentConsumers = 1;
    private boolean useVersion102;
    private Boolean explicitQosEnabled;
    private boolean deliveryPersistent = true;
    private boolean replyToDeliveryPersistent = true;
    private long timeToLive = -1;
    private MessageConverter messageConverter;
    private boolean messageIdEnabled = true;
    private boolean messageTimestampEnabled = true;
    private int priority = -1;
    private boolean transacted;
    private boolean transactedInOut;
    private PlatformTransactionManager transactionManager;
    private String transactionName;
    private int transactionTimeout = -1;
    private boolean preserveMessageQos;
    private long requestMapPurgePollTimeMillis = 1000L;
    private boolean disableReplyTo;
    private boolean eagerLoadingOfProperties;
    private boolean alwaysCopyMessage;
    private boolean useMessageIDAsCorrelationID;
    private JmsProviderMetadata providerMetadata = new JmsProviderMetadata();
    private JmsOperations metadataJmsOperations;
    private String replyToTempDestinationAffinity = REPLYTO_TEMP_DEST_AFFINITY_PER_ENDPOINT;
    private String replyToDestination;
    private String replyToDestinationSelectorName;

    public JmsConfiguration() {
    }

    public JmsConfiguration(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    /**
     * Returns a copy of this configuration
     */
    public JmsConfiguration copy() {
        try {
            return (JmsConfiguration)clone();
        } catch (CloneNotSupportedException e) {
            throw new RuntimeCamelException(e);
        }
    }

    public static interface MessageSentCallback {
        void sent(Message message);
    }

    public static class CamelJmsTemplate extends JmsTemplate {
        private JmsConfiguration config;

        public CamelJmsTemplate(JmsConfiguration config, ConnectionFactory connectionFactory) {
            super(connectionFactory);
            this.config = config;
        }

        public void send(final String destinationName,
                         final MessageCreator messageCreator,
                         final MessageSentCallback callback) throws JmsException {
            execute(new SessionCallback() {
                public Object doInJms(Session session) throws JMSException {
                    Destination destination = resolveDestinationName(session, destinationName);
                    Assert.notNull(messageCreator, "MessageCreator must not be null");
                    MessageProducer producer = createProducer(session, destination);
                    Message message = null;
                    try {
                        message = messageCreator.createMessage(session);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Sending created message: " + message);
                        }
                        doSend(producer, message);
                        if (session.getTransacted() && isSessionLocallyTransacted(session)) {
                            JmsUtils.commitIfNecessary(session);
                        }
                    } finally {
                        JmsUtils.closeMessageProducer(producer);
                    }
                    if (message != null && callback != null) {
                        callback.sent(message);
                    }
                    return null;
                }
            }, false);
        }

        /**
         * Override so we can support preserving the Qos settings that have
         * been set on the message.
         */
        @Override
        protected void doSend(MessageProducer producer, Message message) throws JMSException {
            if (config.isPreserveMessageQos()) {
                long ttl = message.getJMSExpiration();
                if (ttl != 0) {
                    ttl = ttl - System.currentTimeMillis();
                    if (ttl <= 0) {
                        ttl = 1;
                    }
                }
                producer.send(message, message.getJMSDeliveryMode(), message.getJMSPriority(), ttl);
            } else {
                super.doSend(producer, message);
            }
        }
    }

    public static class CamelJmsTeemplate102 extends JmsTemplate102 {
        private JmsConfiguration config;

        public CamelJmsTeemplate102(JmsConfiguration config, ConnectionFactory connectionFactory, boolean pubSubDomain) {
            super(connectionFactory, pubSubDomain);
            this.config = config;
        }

        public void send(final String destinationName,
                final MessageCreator messageCreator,
                final MessageSentCallback callback) throws JmsException {
            execute(new SessionCallback() {
                public Object doInJms(Session session) throws JMSException {
                    Destination destination = resolveDestinationName(session, destinationName);
                    Assert.notNull(messageCreator, "MessageCreator must not be null");
                    MessageProducer producer = createProducer(session, destination);
                    Message message = null;
                    try {
                        message = messageCreator.createMessage(session);
                        if (logger.isDebugEnabled()) {
                            logger.debug("Sending created message: " + message);
                        }
                        doSend(producer, message);
                        if (session.getTransacted() && isSessionLocallyTransacted(session)) {
                            JmsUtils.commitIfNecessary(session);
                        }
                    } finally {
                        JmsUtils.closeMessageProducer(producer);
                    }
                    if (message != null && callback != null) {
                        callback.sent(message);
                    }
                    return null;
                }
            }, false);
        }

        /**
         * Override so we can support preserving the Qos settings that have
         * been set on the message.
         */
        @Override
        protected void doSend(MessageProducer producer, Message message) throws JMSException {
            if (config.isPreserveMessageQos()) {
                long ttl = message.getJMSExpiration();
                if (ttl != 0) {
                    ttl = ttl - System.currentTimeMillis();
                    if (ttl <= 0) {
                        ttl = 1;
                    }
                }
                if (isPubSubDomain()) {
                    ((TopicPublisher) producer).publish(message, message.getJMSDeliveryMode(),
                                                        message.getJMSPriority(), ttl);
                } else {
                    ((QueueSender) producer).send(message, message.getJMSDeliveryMode(),
                                                  message.getJMSPriority(), ttl);
                }
            } else {
                super.doSend(producer, message);
            }
        }
    }

    /**
     * Creates a {@link JmsOperations} object used for request/response using a request
     * timeout value
     */
    public JmsOperations createInOutTemplate(JmsEndpoint endpoint, boolean pubSubDomain, String destination, long requestTimeout) {
        JmsOperations answer = createInOnlyTemplate(endpoint, pubSubDomain, destination);
        if (answer instanceof JmsTemplate && requestTimeout > 0) {
            JmsTemplate jmsTemplate = (JmsTemplate)answer;
            jmsTemplate.setExplicitQosEnabled(true);
            jmsTemplate.setTimeToLive(requestTimeout);
            jmsTemplate.setSessionTransacted(isTransactedInOut());
            if (isTransactedInOut()) {
                jmsTemplate.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
            } else {
                if (acknowledgementMode >= 0) {
                    jmsTemplate.setSessionAcknowledgeMode(acknowledgementMode);
                } else if (acknowledgementModeName != null) {
                    jmsTemplate.setSessionAcknowledgeModeName(acknowledgementModeName);
                } else {
                    jmsTemplate.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
                }
            }
        }
        return answer;
    }

    /**
     * Creates a {@link JmsOperations} object used for one way messaging
     */
    public JmsOperations createInOnlyTemplate(JmsEndpoint endpoint, boolean pubSubDomain, String destination) {

        if (jmsOperations != null) {
            return jmsOperations;
        }

        ConnectionFactory factory = getTemplateConnectionFactory();

        JmsTemplate template = useVersion102
            ? new CamelJmsTeemplate102(this, factory, pubSubDomain)
            : new CamelJmsTemplate(this, factory);

        template.setPubSubDomain(pubSubDomain);
        if (destinationResolver != null) {
            template.setDestinationResolver(destinationResolver);
            if (endpoint instanceof DestinationEndpoint) {
                LOG.debug("You are overloading the destinationResolver property on a DestinationEndpoint; are you sure you want to do that?");
            }
        } else if (endpoint instanceof DestinationEndpoint) {
            DestinationEndpoint destinationEndpoint = (DestinationEndpoint) endpoint;
            template.setDestinationResolver(createDestinationResolver(destinationEndpoint));
        }
        template.setDefaultDestinationName(destination);

        template.setExplicitQosEnabled(isExplicitQosEnabled());
        template.setDeliveryPersistent(deliveryPersistent);
        if (messageConverter != null) {
            template.setMessageConverter(messageConverter);
        }
        template.setMessageIdEnabled(messageIdEnabled);
        template.setMessageTimestampEnabled(messageTimestampEnabled);
        if (priority >= 0) {
            template.setPriority(priority);
        }
        template.setPubSubNoLocal(pubSubNoLocal);
        if (receiveTimeout >= 0) {
            template.setReceiveTimeout(receiveTimeout);
        }
        if (timeToLive >= 0) {
            template.setTimeToLive(timeToLive);
        }

        template.setSessionTransacted(transacted);
        if (transacted) {
            template.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
        } else {
            if (acknowledgementMode >= 0) {
                template.setSessionAcknowledgeMode(acknowledgementMode);
            } else if (acknowledgementModeName != null) {
                template.setSessionAcknowledgeModeName(acknowledgementModeName);
            }
        }
        return template;
    }

    public AbstractMessageListenerContainer createMessageListenerContainer(JmsEndpoint endpoint) {
        AbstractMessageListenerContainer container = chooseMessageListenerContainerImplementation();
        configureMessageListenerContainer(container, endpoint);
        return container;
    }

    public ConnectionFactory getConnectionFactory() {
        if (connectionFactory == null) {
            connectionFactory = createConnectionFactory();
        }
        return connectionFactory;
    }

    /**
     * Sets the default connection factory to be used if a connection factory is
     * not specified for either
     * {@link #setTemplateConnectionFactory(ConnectionFactory)} or
     * {@link #setListenerConnectionFactory(ConnectionFactory)}
     *
     * @param connectionFactory the default connection factory to use
     */
    public void setConnectionFactory(ConnectionFactory connectionFactory) {
        this.connectionFactory = connectionFactory;
    }

    public ConnectionFactory getListenerConnectionFactory() {
        if (listenerConnectionFactory == null) {
            listenerConnectionFactory = createListenerConnectionFactory();
        }
        return listenerConnectionFactory;
    }

    /**
     * Sets the connection factory to be used for consuming messages via the
     * {@link #createMessageListenerContainer(JmsEndpoint)}
     *
     * @param listenerConnectionFactory the connection factory to use for
     *                consuming messages
     */
    public void setListenerConnectionFactory(ConnectionFactory listenerConnectionFactory) {
        this.listenerConnectionFactory = listenerConnectionFactory;
    }

    public ConnectionFactory getTemplateConnectionFactory() {
        if (templateConnectionFactory == null) {
            templateConnectionFactory = createTemplateConnectionFactory();
        }
        return templateConnectionFactory;
    }

    /**
     * Sets the connection factory to be used for sending messages via the
     * {@link JmsTemplate} via {@link #createInOnlyTemplate(JmsEndpoint,boolean, String)}
     *
     * @param templateConnectionFactory the connection factory for sending
     *                messages
     */
    public void setTemplateConnectionFactory(ConnectionFactory templateConnectionFactory) {
        this.templateConnectionFactory = templateConnectionFactory;
    }

    public boolean isUseVersion102() {
        return useVersion102;
    }

    public void setUseVersion102(boolean useVersion102) {
        this.useVersion102 = useVersion102;
    }

    public boolean isAutoStartup() {
        return autoStartup;
    }

    public void setAutoStartup(boolean autoStartup) {
        this.autoStartup = autoStartup;
    }

    public boolean isAcceptMessagesWhileStopping() {
        return acceptMessagesWhileStopping;
    }

    public void setAcceptMessagesWhileStopping(boolean acceptMessagesWhileStopping) {
        this.acceptMessagesWhileStopping = acceptMessagesWhileStopping;
    }

    public String getClientId() {
        return clientId;
    }

    public void setClientId(String consumerClientId) {
        this.clientId = consumerClientId;
    }

    public String getDurableSubscriptionName() {
        return durableSubscriptionName;
    }

    public void setDurableSubscriptionName(String durableSubscriptionName) {
        this.durableSubscriptionName = durableSubscriptionName;
    }

    public ExceptionListener getExceptionListener() {
        return exceptionListener;
    }

    public void setExceptionListener(ExceptionListener exceptionListener) {
        this.exceptionListener = exceptionListener;
    }

    public boolean isSubscriptionDurable() {
        return subscriptionDurable;
    }

    public void setSubscriptionDurable(boolean subscriptionDurable) {
        this.subscriptionDurable = subscriptionDurable;
    }

    public String getAcknowledgementModeName() {
        return acknowledgementModeName;
    }

    public void setAcknowledgementModeName(String consumerAcknowledgementMode) {
        this.acknowledgementModeName = consumerAcknowledgementMode;
        this.acknowledgementMode = -1;
    }

    public boolean isExposeListenerSession() {
        return exposeListenerSession;
    }

    public void setExposeListenerSession(boolean exposeListenerSession) {
        this.exposeListenerSession = exposeListenerSession;
    }

    public TaskExecutor getTaskExecutor() {
        return taskExecutor;
    }

    public void setTaskExecutor(TaskExecutor taskExecutor) {
        this.taskExecutor = taskExecutor;
    }

    public boolean isPubSubNoLocal() {
        return pubSubNoLocal;
    }

    public void setPubSubNoLocal(boolean pubSubNoLocal) {
        this.pubSubNoLocal = pubSubNoLocal;
    }

    public int getConcurrentConsumers() {
        return concurrentConsumers;
    }

    public void setConcurrentConsumers(int concurrentConsumers) {
        this.concurrentConsumers = concurrentConsumers;
    }

    public int getMaxMessagesPerTask() {
        return maxMessagesPerTask;
    }

    public void setMaxMessagesPerTask(int maxMessagesPerTask) {
        this.maxMessagesPerTask = maxMessagesPerTask;
    }

    public ServerSessionFactory getServerSessionFactory() {
        return serverSessionFactory;
    }

    public void setServerSessionFactory(ServerSessionFactory serverSessionFactory) {
        this.serverSessionFactory = serverSessionFactory;
    }

    public int getCacheLevel() {
        return cacheLevel;
    }

    public void setCacheLevel(int cacheLevel) {
        this.cacheLevel = cacheLevel;
    }

    public String getCacheLevelName() {
        return cacheLevelName;
    }

    public void setCacheLevelName(String cacheName) {
        this.cacheLevelName = cacheName;
    }

    public long getRecoveryInterval() {
        return recoveryInterval;
    }

    public void setRecoveryInterval(long recoveryInterval) {
        this.recoveryInterval = recoveryInterval;
    }

    public long getReceiveTimeout() {
        return receiveTimeout;
    }

    public void setReceiveTimeout(long receiveTimeout) {
        this.receiveTimeout = receiveTimeout;
    }

    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;
    }

    public void setTransactionManager(PlatformTransactionManager transactionManager) {
        this.transactionManager = transactionManager;
    }

    public String getTransactionName() {
        return transactionName;
    }

    public void setTransactionName(String transactionName) {
        this.transactionName = transactionName;
    }

    public int getTransactionTimeout() {
        return transactionTimeout;
    }

    public void setTransactionTimeout(int transactionTimeout) {
        this.transactionTimeout = transactionTimeout;
    }

    public int getIdleTaskExecutionLimit() {
        return idleTaskExecutionLimit;
    }

    public void setIdleTaskExecutionLimit(int idleTaskExecutionLimit) {
        this.idleTaskExecutionLimit = idleTaskExecutionLimit;
    }

    public int getMaxConcurrentConsumers() {
        return maxConcurrentConsumers;
    }

    public void setMaxConcurrentConsumers(int maxConcurrentConsumers) {
        this.maxConcurrentConsumers = maxConcurrentConsumers;
    }

    public boolean isExplicitQosEnabled() {
        return explicitQosEnabled != null ? explicitQosEnabled : false;
    }

    public void setExplicitQosEnabled(boolean explicitQosEnabled) {
        this.explicitQosEnabled = explicitQosEnabled;
    }

    public boolean isDeliveryPersistent() {
        return deliveryPersistent;
    }

    public void setDeliveryPersistent(boolean deliveryPersistent) {
        this.deliveryPersistent = deliveryPersistent;
        configuredQoS();
    }

    public boolean isReplyToDeliveryPersistent() {
        return replyToDeliveryPersistent;
    }

    public void setReplyToDeliveryPersistent(boolean replyToDeliveryPersistent) {
        this.replyToDeliveryPersistent = replyToDeliveryPersistent;
    }

    public long getTimeToLive() {
        return timeToLive;
    }

    public void setTimeToLive(long timeToLive) {
        this.timeToLive = timeToLive;
        configuredQoS();
    }

    public MessageConverter getMessageConverter() {
        return messageConverter;
    }

    public void setMessageConverter(MessageConverter messageConverter) {
        this.messageConverter = messageConverter;
    }

    public boolean isMessageIdEnabled() {
        return messageIdEnabled;
    }

    public void setMessageIdEnabled(boolean messageIdEnabled) {
        this.messageIdEnabled = messageIdEnabled;
    }

    public boolean isMessageTimestampEnabled() {
        return messageTimestampEnabled;
    }

    public void setMessageTimestampEnabled(boolean messageTimestampEnabled) {
        this.messageTimestampEnabled = messageTimestampEnabled;
    }

    public int getPriority() {
        return priority;
    }

    public void setPriority(int priority) {
        this.priority = priority;
        configuredQoS();
    }

    public ConsumerType getConsumerType() {
        return consumerType;
    }

    public void setConsumerType(ConsumerType consumerType) {
        this.consumerType = consumerType;
    }

    public int getAcknowledgementMode() {
        return acknowledgementMode;
    }

    public void setAcknowledgementMode(int consumerAcknowledgementMode) {
        this.acknowledgementMode = consumerAcknowledgementMode;
        this.acknowledgementModeName = null;
    }

    public boolean isTransacted() {
        return transacted;
    }

    public void setTransacted(boolean consumerTransacted) {
        this.transacted = consumerTransacted;
    }

    /**
     * Should InOut operations (request reply) default to using transacted mode?
     *
     * By default this is false as you need to commit the outgoing request before you can consume the input
     *
     * @return
     */
    public boolean isTransactedInOut() {
        return transactedInOut;
    }

    public void setTransactedInOut(boolean transactedInOut) {
        this.transactedInOut = transactedInOut;
    }

    public boolean isEagerLoadingOfProperties() {
        return eagerLoadingOfProperties;
    }

    /**
     * Enables eager loading of JMS properties as soon as a message is loaded
     * which generally is inefficient as the JMS properties may not be required
     * but sometimes can catch early any issues with the underlying JMS provider
     * and the use of JMS properties
     *
     * @param eagerLoadingOfProperties whether or not to enable eager loading of
     *                JMS properties on inbound messages
     */
    public void setEagerLoadingOfProperties(boolean eagerLoadingOfProperties) {
        this.eagerLoadingOfProperties = eagerLoadingOfProperties;
    }

    public boolean isDisableReplyTo() {
        return disableReplyTo;
    }

    /**
     * Disables the use of the JMSReplyTo header for consumers so that inbound
     * messages are treated as InOnly rather than InOut requests.
     *
     * @param disableReplyTo whether or not to disable the use of JMSReplyTo
     *                header indicating an InOut
     */
    public void setDisableReplyTo(boolean disableReplyTo) {
        this.disableReplyTo = disableReplyTo;
    }

    /**
     * Set to true if you want to send message using the QoS settings specified
     * on the message. Normally the QoS settings used are the one configured on
     * this Object.
     */
    public void setPreserveMessageQos(boolean preserveMessageQos) {
        this.preserveMessageQos = preserveMessageQos;
    }

    public JmsOperations getJmsOperations() {
        return jmsOperations;
    }

    public void setJmsOperations(JmsOperations jmsOperations) {
        this.jmsOperations = jmsOperations;
    }

    public DestinationResolver getDestinationResolver() {
        return destinationResolver;
    }

    public void setDestinationResolver(DestinationResolver destinationResolver) {
        this.destinationResolver = destinationResolver;
    }

    public long getRequestMapPurgePollTimeMillis() {
        return requestMapPurgePollTimeMillis;
    }

    /**
     * Sets the frequency that the requestMap for InOut exchanges is purged for
     * timed out message exchanges
     */
    public void setRequestMapPurgePollTimeMillis(long requestMapPurgePollTimeMillis) {
        this.requestMapPurgePollTimeMillis = requestMapPurgePollTimeMillis;
    }
    public JmsProviderMetadata getProviderMetadata() {
        return providerMetadata;
    }

    /**
     * Allows the provider metadata to be explicitly configured. Typically this is not required
     * and Camel will auto-detect the provider metadata from the underlying provider.
     */
    public void setProviderMetadata(JmsProviderMetadata providerMetadata) {
        this.providerMetadata = providerMetadata;
    }

    public JmsOperations getMetadataJmsOperations(JmsEndpoint endpoint) {
        if (metadataJmsOperations == null) {
            metadataJmsOperations = getJmsOperations();
            if (metadataJmsOperations == null) {
                metadataJmsOperations = createInOnlyTemplate(endpoint, false, null);
            }
        }
        return metadataJmsOperations;
    }

    /**
     * Sets the {@link JmsOperations} used to deduce the {@link JmsProviderMetadata} details which if none
     * is customized one is lazily created on demand
     */
    public void setMetadataJmsOperations(JmsOperations metadataJmsOperations) {
        this.metadataJmsOperations = metadataJmsOperations;
    }

    public static DestinationResolver createDestinationResolver(final DestinationEndpoint destinationEndpoint) {
        return new DestinationResolver() {
            public Destination resolveDestinationName(Session session, String destinationName, boolean pubSubDomain) throws JMSException {
                return destinationEndpoint.getJmsDestination(session);
            }
        };
    }

    protected void configureMessageListenerContainer(AbstractMessageListenerContainer container,
                                                     JmsEndpoint endpoint) {
        container.setConnectionFactory(getListenerConnectionFactory());
        if (endpoint instanceof DestinationEndpoint) {
            container.setDestinationResolver(createDestinationResolver((DestinationEndpoint) endpoint));
        } else if (destinationResolver != null) {
            container.setDestinationResolver(destinationResolver);
        }
        if (autoStartup) {
            container.setAutoStartup(true);
        }
        if (clientId != null) {
            container.setClientId(clientId);
        }
        container.setSubscriptionDurable(subscriptionDurable);
        if (durableSubscriptionName != null) {
            container.setDurableSubscriptionName(durableSubscriptionName);
        }

        if (durableSubscriptionName != null && clientId != null) {
            container.setSubscriptionDurable(true);
        }

        if (exceptionListener != null) {
            container.setExceptionListener(exceptionListener);
        }

        container.setAcceptMessagesWhileStopping(acceptMessagesWhileStopping);
        container.setExposeListenerSession(exposeListenerSession);
        container.setSessionTransacted(transacted);
        if (transacted) {
            container.setSessionAcknowledgeMode(Session.SESSION_TRANSACTED);
        } else {
            if (acknowledgementMode >= 0) {
                container.setSessionAcknowledgeMode(acknowledgementMode);
            } else if (acknowledgementModeName != null) {
                container.setSessionAcknowledgeModeName(acknowledgementModeName);
            }
        }

        if (endpoint.getSelector() != null && endpoint.getSelector().length() != 0) {
            container.setMessageSelector(endpoint.getSelector());
        }

        if (container instanceof DefaultMessageListenerContainer) {
            DefaultMessageListenerContainer listenerContainer = (DefaultMessageListenerContainer)container;
            if (concurrentConsumers >= 0) {
                listenerContainer.setConcurrentConsumers(concurrentConsumers);
            }

            if (cacheLevel >= 0) {
                listenerContainer.setCacheLevel(cacheLevel);
            } else if (cacheLevelName != null) {
                listenerContainer.setCacheLevelName(cacheLevelName);
            } else {
                listenerContainer.setCacheLevel(defaultCacheLevel(endpoint));
            }

            if (idleTaskExecutionLimit >= 0) {
                listenerContainer.setIdleTaskExecutionLimit(idleTaskExecutionLimit);
            }
            if (maxConcurrentConsumers >= 0) {
                listenerContainer.setMaxConcurrentConsumers(maxConcurrentConsumers);
            }
            if (maxMessagesPerTask >= 0) {
                listenerContainer.setMaxMessagesPerTask(maxMessagesPerTask);
            }
            listenerContainer.setPubSubNoLocal(pubSubNoLocal);
            if (receiveTimeout >= 0) {
                listenerContainer.setReceiveTimeout(receiveTimeout);
            }
            if (recoveryInterval >= 0) {
                listenerContainer.setRecoveryInterval(recoveryInterval);
            }
            if (taskExecutor != null) {
                listenerContainer.setTaskExecutor(taskExecutor);
            }
            PlatformTransactionManager tm = getTransactionManager();
            if (tm != null && transacted) {
                listenerContainer.setTransactionManager(tm);
            } else if (transacted) {
                throw new IllegalArgumentException("Property transacted is enabled but a transactionManager was not injected!");
            }
            if (transactionName != null) {
                listenerContainer.setTransactionName(transactionName);
            }
            if (transactionTimeout >= 0) {
                listenerContainer.setTransactionTimeout(transactionTimeout);
            }
        } else if (container instanceof ServerSessionMessageListenerContainer) {
            ServerSessionMessageListenerContainer listenerContainer = (ServerSessionMessageListenerContainer)container;
            if (maxMessagesPerTask >= 0) {
                listenerContainer.setMaxMessagesPerTask(maxMessagesPerTask);
            }
            if (serverSessionFactory != null) {
                listenerContainer.setServerSessionFactory(serverSessionFactory);
            }
        } else if (container instanceof SimpleMessageListenerContainer) {
            SimpleMessageListenerContainer listenerContainer = (SimpleMessageListenerContainer)container;
            if (concurrentConsumers >= 0) {
                listenerContainer.setConcurrentConsumers(concurrentConsumers);
            }
            listenerContainer.setPubSubNoLocal(pubSubNoLocal);
            if (taskExecutor != null) {
                listenerContainer.setTaskExecutor(taskExecutor);
            }
        }
    }

    public void configure(EndpointMessageListener listener) {
        if (isDisableReplyTo()) {
            listener.setDisableReplyTo(true);
        }
        if (isEagerLoadingOfProperties()) {
            listener.setEagerLoadingOfProperties(true);
        }
        JmsOperations operations = listener.getTemplate();
        if (operations instanceof JmsTemplate) {
            JmsTemplate template = (JmsTemplate)operations;
            template.setDeliveryPersistent(isReplyToDeliveryPersistent());
        }
    }

    public AbstractMessageListenerContainer chooseMessageListenerContainerImplementation() {
        switch (consumerType) {
        case Simple:
            return isUseVersion102()
                ? new SimpleMessageListenerContainer102() : new SimpleMessageListenerContainer();
        case ServerSessionPool:
            return isUseVersion102()
                ? new ServerSessionMessageListenerContainer102()
                : new ServerSessionMessageListenerContainer();
        case Default:
            return isUseVersion102()
                ? new DefaultMessageListenerContainer102() : new DefaultMessageListenerContainer();
        default:
            throw new IllegalArgumentException("Unknown consumer type: " + consumerType);
        }
    }

    /**
     * Defaults the JMS cache level if none is explicitly specified. Note that
     * due to this <a
     * Bug</a> we cannot use CACHE_CONSUMER by default (which we should do as
     * its most efficient) unless the spring version is 2.5.1 or later. Instead
     * we use CACHE_CONNECTION - part from for non-durable topics which must use
     * CACHE_CONSUMER to avoid missing messages (due to the consumer being
     * created and destroyed per message).
     *
     * @param endpoint the endpoint
     * @return the cacne level
     */
    protected int defaultCacheLevel(JmsEndpoint endpoint) {
        if (PackageHelper.isValidVersion("org.springframework.jms", 2.51D)) {
            return DefaultMessageListenerContainer.CACHE_CONSUMER;
        } else {
            if (endpoint.isPubSubDomain() && !isSubscriptionDurable()) {
                return DefaultMessageListenerContainer.CACHE_CONSUMER;
            } else {
                return DefaultMessageListenerContainer.CACHE_CONNECTION;
            }
        }
    }

    /**
     * Factory method which allows derived classes to customize the lazy
     * creation
     */
    protected ConnectionFactory createConnectionFactory() {
        ObjectHelper.notNull(connectionFactory, "connectionFactory");
        return null;
    }

    /**
     * Factory method which allows derived classes to customize the lazy
     * creation
     */
    protected ConnectionFactory createListenerConnectionFactory() {
        return getConnectionFactory();
    }

    /**
     * Factory method which allows derived classes to customize the lazy
     * creation
     */
    protected ConnectionFactory createTemplateConnectionFactory() {
        return getConnectionFactory();
    }

    public boolean isPreserveMessageQos() {
        return preserveMessageQos;
    }

    /**
     * When one of the QoS properties are configured such as {@link #setDeliveryPersistent(boolean)},
     * {@link #setPriority(int)} or {@link #setTimeToLive(long)} then we should auto default the
     * setting of {@link #setExplicitQosEnabled(boolean)} if its not been configured yet
     */
    protected void configuredQoS() {
        if (explicitQosEnabled == null) {
            explicitQosEnabled = true;
        }
    }

    public boolean isAlwaysCopyMessage() {
        return alwaysCopyMessage;
    }

    public void setAlwaysCopyMessage(boolean alwaysCopyMessage) {
        this.alwaysCopyMessage = alwaysCopyMessage;
    }

    public boolean isUseMessageIDAsCorrelationID() {
        return useMessageIDAsCorrelationID;
    }

    public void setUseMessageIDAsCorrelationID(boolean useMessageIDAsCorrelationID) {
        this.useMessageIDAsCorrelationID = useMessageIDAsCorrelationID;
    }

    public String getReplyToTempDestinationAffinity() {
        return replyToTempDestinationAffinity;
    }

    public void setReplyToTempDestinationAffinity(
            String replyToTempDestinationAffinity) {
        this.replyToTempDestinationAffinity = replyToTempDestinationAffinity;
    }

    public long getRequestTimeout() {
        return requestTimeout;
    }

    /**
     * Sets the timeout in milliseconds which requests should timeout after
     */
    public void setRequestTimeout(long requestTimeout) {
        this.requestTimeout = requestTimeout;
    }

    public String getReplyTo() {
        return replyToDestination;
    }

    public void setReplyTo(String replyToDestination) {
        if (!replyToDestination.startsWith(QUEUE_PREFIX)) {
            throw new IllegalArgumentException("ReplyTo destination value has to be of type queue; "
                                              + "e.g: \"queue:replyQueue\"");
        }
        this.replyToDestination =
            removeStartingCharacters(replyToDestination.substring(QUEUE_PREFIX.length()), '/');
    }

    public String getReplyToDestinationSelectorName() {
        return replyToDestinationSelectorName;
    }

    public void setReplyToDestinationSelectorName(String replyToDestinationSelectorName) {
        this.replyToDestinationSelectorName = replyToDestinationSelectorName;
        if (replyToDestinationSelectorName != null) {
            setAlwaysCopyMessage(true);

FUNC_DECL TRY CATCH RETURN THROW_RAISE CALL CALL

FUNC_DECL RETURN VAR_DECL PARAM CALL CALL

    public boolean isUseVersion102() {
        return useVersion102;

    public void setUseVersion102(boolean useVersion102) {
        this.useVersion102 = useVersion102;

    public boolean isAutoStartup() {
        return autoStartup;

    public void setAutoStartup(boolean autoStartup) {
        this.autoStartup = autoStartup;

    public boolean isAcceptMessagesWhileStopping() {
        return acceptMessagesWhileStopping;

    public String getClientId() {
        return clientId;

    public void setClientId(String consumerClientId) {
        this.clientId = consumerClientId;

    public String getDurableSubscriptionName() {
        return durableSubscriptionName;

    public ExceptionListener getExceptionListener() {
        return exceptionListener;

    public boolean isSubscriptionDurable() {
        return subscriptionDurable;

    public String getAcknowledgementModeName() {
        return acknowledgementModeName;

    public boolean isExposeListenerSession() {
        return exposeListenerSession;

    public TaskExecutor getTaskExecutor() {
        return taskExecutor;

    public boolean isPubSubNoLocal() {
        return pubSubNoLocal;

    public void setPubSubNoLocal(boolean pubSubNoLocal) {
        this.pubSubNoLocal = pubSubNoLocal;

    public int getConcurrentConsumers() {
        return concurrentConsumers;

    public int getMaxMessagesPerTask() {
        return maxMessagesPerTask;

    public ServerSessionFactory getServerSessionFactory() {
        return serverSessionFactory;

    public int getCacheLevel() {
        return cacheLevel;

    public void setCacheLevel(int cacheLevel) {
        this.cacheLevel = cacheLevel;

    public String getCacheLevelName() {
        return cacheLevelName;

    public void setCacheLevelName(String cacheName) {
        this.cacheLevelName = cacheName;

    public long getRecoveryInterval() {
        return recoveryInterval;

    public long getReceiveTimeout() {
        return receiveTimeout;

    public void setReceiveTimeout(long receiveTimeout) {
        this.receiveTimeout = receiveTimeout;

    public PlatformTransactionManager getTransactionManager() {
        return transactionManager;

    public String getTransactionName() {
        return transactionName;

    public int getTransactionTimeout() {
        return transactionTimeout;

    public int getIdleTaskExecutionLimit() {
        return idleTaskExecutionLimit;

    public int getMaxConcurrentConsumers() {
        return maxConcurrentConsumers;

    public boolean isDeliveryPersistent() {
        return deliveryPersistent;

    public boolean isReplyToDeliveryPersistent() {
        return replyToDeliveryPersistent;

    public long getTimeToLive() {
        return timeToLive;

    public MessageConverter getMessageConverter() {
        return messageConverter;

    public boolean isMessageIdEnabled() {
        return messageIdEnabled;

    public boolean isMessageTimestampEnabled() {
        return messageTimestampEnabled;

    public int getPriority() {
        return priority;

    public void setPriority(int priority) {
        this.priority = priority;
        configuredQoS();

    public ConsumerType getConsumerType() {
        return consumerType;

    public void setConsumerType(ConsumerType consumerType) {
        this.consumerType = consumerType;

    public int getAcknowledgementMode() {
        return acknowledgementMode;

    public boolean isTransacted() {
        return transacted;

    public boolean isTransactedInOut() {
        return transactedInOut;

    public boolean isEagerLoadingOfProperties() {
        return eagerLoadingOfProperties;

    public boolean isDisableReplyTo() {
        return disableReplyTo;

    public JmsOperations getJmsOperations() {
        return jmsOperations;

    public DestinationResolver getDestinationResolver() {
        return destinationResolver;

    public long getRequestMapPurgePollTimeMillis() {
        return requestMapPurgePollTimeMillis;

    public JmsProviderMetadata getProviderMetadata() {
        return providerMetadata;

FUNC_DECL RETURN PARAM CALL FUNC_DECL RETURN PARAM PARAM PARAM CALL

FUNC_DECL SWITCH THROW_RAISE RETURN RETURN RETURN CALL COND_EXPR COND_EXPR COND_EXPR BINARY_OP CALL CALL CALL CALL CALL CALL CALL CALL CALL LITERAL_ANCHOR

FUNC_DECL IF PARAM CALL IF RETURN LITERAL_ANCHOR LITERAL_ANCHOR BINARY_OP RETURN RETURN CALL CALL

    public boolean isPreserveMessageQos() {
        return preserveMessageQos;

    public boolean isAlwaysCopyMessage() {
        return alwaysCopyMessage;

    public boolean isUseMessageIDAsCorrelationID() {
        return useMessageIDAsCorrelationID;

    public String getReplyToTempDestinationAffinity() {
        return replyToTempDestinationAffinity;

    public long getRequestTimeout() {
        return requestTimeout;

    public void setRequestTimeout(long requestTimeout) {
        this.requestTimeout = requestTimeout;

    public String getReplyTo() {
        return replyToDestination;

    public String getReplyToDestinationSelectorName() {
        return replyToDestinationSelectorName;


org.gjt.sp.jedit.pluginmgr.PluginList

class PluginList
{
	/**
	 * Magic numbers used for auto-detecting GZIP files.
	 */
	public static final int GZIP_MAGIC_1 = 0x1f;
	public static final int GZIP_MAGIC_2 = 0x8b;

	Vector plugins;
	Hashtable pluginHash;
	Vector pluginSets;

	//{{{ PluginList constructor
	PluginList() throws Exception
	{
		plugins = new Vector();
		pluginHash = new Hashtable();
		pluginSets = new Vector();

		String path = jEdit.getProperty("plugin-manager.export-url");
		String id = jEdit.getProperty("plugin-manager.mirror.id");
		if (!id.equals(MirrorList.Mirror.NONE))
			path += "?mirror="+id;
		PluginListHandler handler = new PluginListHandler(this,path);
		XmlParser parser = new XmlParser();
		parser.setHandler(handler);

		InputStream in = new BufferedInputStream(new URL(path).openStream());
		try
		{
			if(in.markSupported())
			{
				in.mark(2);
				int b1 = in.read();
				int b2 = in.read();
				in.reset();

				if(b1 == GZIP_MAGIC_1 && b2 == GZIP_MAGIC_2)
					in = new GZIPInputStream(in);
			}

			parser.parse(null,null,new InputStreamReader(in,"UTF8"));
		}
		finally
		{
			in.close();
		}
	} //}}}

	//{{{ addPlugin() method
	void addPlugin(Plugin plugin)
	{
		plugin.checkIfInstalled();
		plugins.addElement(plugin);
		pluginHash.put(plugin.name,plugin);
	} //}}}

	//{{{ addPluginSet() method
	void addPluginSet(PluginSet set)
	{
		pluginSets.addElement(set);
	} //}}}

	//{{{ finished() method
	void finished()
	{
		// after the entire list is loaded, fill out plugin field
		// in dependencies
		for(int i = 0; i < plugins.size(); i++)
		{
			Plugin plugin = (Plugin)plugins.elementAt(i);
			for(int j = 0; j < plugin.branches.size(); j++)
			{
				Branch branch = (Branch)plugin.branches.elementAt(j);
				for(int k = 0; k < branch.deps.size(); k++)
				{
					Dependency dep = (Dependency)branch.deps.elementAt(k);
					if(dep.what.equals("plugin"))
						dep.plugin = (Plugin)pluginHash.get(dep.pluginName);
				}
			}
		}
	} //}}}

	//{{{ dump() method
	void dump()
	{
		for(int i = 0; i < plugins.size(); i++)
		{
			System.err.println((Plugin)plugins.elementAt(i));
			System.err.println();
		}
	} //}}}

	//{{{ PluginSet class
	static class PluginSet
	{
		String name;
		String description;
		Vector plugins = new Vector();

		public String toString()
		{
			return plugins.toString();
		}
	} //}}}

	//{{{ Plugin class
	static public class Plugin
	{
		String jar;
		String name;
		String description;
		String author;
		Vector branches = new Vector();
		//String installed;
		//String installedVersion;

		void checkIfInstalled()
		{
			/* // check if the plugin is already installed.
			// this is a bit of hack
			PluginJAR[] jars = jEdit.getPluginJARs();
			for(int i = 0; i < jars.length; i++)
			{
				String path = jars[i].getPath();
				if(!new File(path).exists())
					continue;

				if(MiscUtilities.getFileName(path).equals(jar))
				{
					installed = path;

					EditPlugin plugin = jars[i].getPlugin();
					if(plugin != null)
					{
						installedVersion = jEdit.getProperty(
							"plugin." + plugin.getClassName()
							+ ".version");
					}
					break;
				}
			}

			String[] notLoaded = jEdit.getNotLoadedPluginJARs();
			for(int i = 0; i < notLoaded.length; i++)
			{
				String path = notLoaded[i];

				if(MiscUtilities.getFileName(path).equals(jar))
				{
					installed = path;
					break;
				}
			} */
		}

		String getInstalledVersion()
		{
			PluginJAR[] jars = jEdit.getPluginJARs();
			for(int i = 0; i < jars.length; i++)
			{
				String path = jars[i].getPath();

				if(MiscUtilities.getFileName(path).equals(jar))
				{
					EditPlugin plugin = jars[i].getPlugin();
					if(plugin != null)
					{
						return jEdit.getProperty(
							"plugin." + plugin.getClassName()
							+ ".version");
					}
					else
						return null;
				}
			}

			return null;
		}

		String getInstalledPath()
		{
			PluginJAR[] jars = jEdit.getPluginJARs();
			for(int i = 0; i < jars.length; i++)
			{
				String path = jars[i].getPath();

				if(MiscUtilities.getFileName(path).equals(jar))
					return path;
			}

			return null;
		}

		/**
		 * Find the first branch compatible with the running jEdit release.
		 */
		Branch getCompatibleBranch()
		{
			for(int i = 0; i < branches.size(); i++)
			{
				Branch branch = (Branch)branches.elementAt(i);
				if(branch.canSatisfyDependencies())
					return branch;
			}

			return null;
		}

		boolean canBeInstalled()
		{
			Branch branch = getCompatibleBranch();
			return branch != null && !branch.obsolete
				&& branch.canSatisfyDependencies();
		}

		void install(Roster roster, String installDirectory, boolean downloadSource)
		{
			String installed = getInstalledPath();

			Branch branch = getCompatibleBranch();
			if(branch.obsolete)
			{
				if(installed != null)
					roster.addRemove(installed);
				return;
			}

			//branch.satisfyDependencies(roster,installDirectory,
			//	downloadSource);

			if(installed != null)
			{
				installDirectory = MiscUtilities.getParentOfPath(
					installed);
			}

			roster.addInstall(
				installed,
				(downloadSource ? branch.downloadSource : branch.download),
				installDirectory,
				(downloadSource ? branch.downloadSourceSize : branch.downloadSize));

		}

		public String toString()
		{
			return name;
		}
	} //}}}

	//{{{ Branch class
	static class Branch
	{
		String version;
		String date;
		int downloadSize;
		String download;
		int downloadSourceSize;
		String downloadSource;
		boolean obsolete;
		Vector deps = new Vector();

		boolean canSatisfyDependencies()
		{
			for(int i = 0; i < deps.size(); i++)
			{
				Dependency dep = (Dependency)deps.elementAt(i);
				if(!dep.canSatisfy())
					return false;
			}

			return true;
		}

		void satisfyDependencies(Roster roster, String installDirectory,
			boolean downloadSource)
		{
			for(int i = 0; i < deps.size(); i++)
			{
				Dependency dep = (Dependency)deps.elementAt(i);
				dep.satisfy(roster,installDirectory,downloadSource);
			}
		}

		public String toString()
		{
			return "[version=" + version + ",download=" + download
				+ ",obsolete=" + obsolete + ",deps=" + deps + "]";
		}
	} //}}}

	//{{{ Dependency class
	static class Dependency
	{
		String what;
		String from;
		String to;
		// only used if what is "plugin"
		String pluginName;
		Plugin plugin;

		Dependency(String what, String from, String to, String pluginName)
		{
			this.what = what;
			this.from = from;
			this.to = to;
			this.pluginName = pluginName;
		}

		boolean isSatisfied()
		{
			if(what.equals("plugin"))
			{
				for(int i = 0; i < plugin.branches.size(); i++)
				{
					String installedVersion = plugin.getInstalledVersion();
					if(installedVersion != null
						&&
					(from == null || MiscUtilities.compareStrings(
						installedVersion,from,false) >= 0)
						&&
					   (to == null || MiscUtilities.compareStrings(
					   	installedVersion,to,false) <= 0))
					{
						return true;
					}
				}

				return false;
			}
			else if(what.equals("jdk"))
			{
				String javaVersion = System.getProperty("java.version").substring(0,3);

				if((from == null || MiscUtilities.compareStrings(
					javaVersion,from,false) >= 0)
					&&
				   (to == null || MiscUtilities.compareStrings(
				   	javaVersion,to,false) <= 0))
					return true;
				else
					return false;
			}
			else if(what.equals("jedit"))
			{
				String build = jEdit.getBuild();

				if((from == null || MiscUtilities.compareStrings(
					build,from,false) >= 0)
					&&
				   (to == null || MiscUtilities.compareStrings(
				   	build,to,false) <= 0))
					return true;
				else
					return false;
			}
			else
			{
				Log.log(Log.ERROR,this,"Invalid dependency: " + what);
				return false;
			}
		}

		boolean canSatisfy()
		{
			if(isSatisfied())
				return true;
			else if(what.equals("plugin"))
			{
				return plugin.canBeInstalled();
			}
			else
				return false;
		}

		void satisfy(Roster roster, String installDirectory,
			boolean downloadSource)
		{
			if(what.equals("plugin"))
			{
				String installedVersion = plugin.getInstalledVersion();
				for(int i = 0; i < plugin.branches.size(); i++)
				{
					Branch branch = (Branch)plugin.branches
						.elementAt(i);
					if((installedVersion == null
						||
					MiscUtilities.compareStrings(
						installedVersion,branch.version,false) < 0)
						&&
					(from == null || MiscUtilities.compareStrings(
						branch.version,from,false) >= 0)
						&&
					   (to == null || MiscUtilities.compareStrings(
					   	branch.version,to,false) <= 0))
					{
						plugin.install(roster,installDirectory,
							downloadSource);
						return;
					}
				}
			}
		}

		public String toString()
		{
			return "[what=" + what + ",from=" + from
				+ ",to=" + to + ",plugin=" + plugin + "]";


public class RedundentExprEliminator extends XSLTVisitor
{
  Vector m_paths;
  Vector m_absPaths;
  boolean m_isSameContext;
  AbsPathChecker m_absPathChecker = new AbsPathChecker();

  private static int m_uniquePseudoVarID = 1;
  static final String PSUEDOVARNAMESPACE = Constants.S_VENDORURL+"/xalan/psuedovar";

  public static final boolean DEBUG = false;
  public static final boolean DIAGNOSE_NUM_PATHS_REDUCED = false;
  public static final boolean DIAGNOSE_MULTISTEPLIST = false;

  /**
   * So we can reuse it over and over again.
   */
  VarNameCollector m_varNameCollector = new VarNameCollector();

  /**
   * Construct a RedundentExprEliminator.
   */
  public RedundentExprEliminator()
  {
    m_isSameContext = true;
    m_absPaths = new Vector();
    m_paths = null;
  }

  /**
   * Method to be called after the all expressions within an
   * node context have been visited.  It eliminates redundent
   * expressions by creating a variable in the psuedoVarRecipient
   * for each redundent expression, and then rewriting the redundent
   * expression to be a variable reference.
   *
   * @param psuedoVarRecipient The recipient of the psuedo vars.  The
   * variables will be inserted as first children of the element, before
   * any existing variables.
   */
  public void eleminateRedundentLocals(ElemTemplateElement psuedoVarRecipient)
  {
    eleminateRedundent(psuedoVarRecipient, m_paths);
  }

  /**
   * Method to be called after the all global expressions within a stylesheet
   * have been collected.  It eliminates redundent
   * expressions by creating a variable in the psuedoVarRecipient
   * for each redundent expression, and then rewriting the redundent
   * expression to be a variable reference.
   *
   */
  public void eleminateRedundentGlobals(StylesheetRoot stylesheet)
  {
    eleminateRedundent(stylesheet, m_absPaths);
  }

  /**
   * Method to be called after the all expressions within an
   * node context have been visited.  It eliminates redundent
   * expressions by creating a variable in the psuedoVarRecipient
   * for each redundent expression, and then rewriting the redundent
   * expression to be a variable reference.
   *
   * @param psuedoVarRecipient The owner of the subtree from where the
   *                           paths were collected.
   * @param paths A vector of paths that hold ExpressionOwner objects,
   *              which must yield LocationPathIterators.
   */
  protected void eleminateRedundent(ElemTemplateElement psuedoVarRecipient, Vector paths)
  {
    int n = paths.size();
    int numPathsEliminated = 0;
    int numUniquePathsEliminated = 0;
    for (int i = 0; i < n; i++)
    {
      ExpressionOwner owner = (ExpressionOwner) paths.elementAt(i);
      if (null != owner)
      {
        int found = findAndEliminateRedundant(i + 1, i, owner, psuedoVarRecipient, paths);
        if (found > 0)
                  numUniquePathsEliminated++;
        numPathsEliminated += found;
      }
    }

    eleminateSharedPartialPaths(psuedoVarRecipient, paths);

    if(DIAGNOSE_NUM_PATHS_REDUCED)
		diagnoseNumPaths(paths, numPathsEliminated, numUniquePathsEliminated);
  }

  /**
   * Eliminate the shared partial paths in the expression list.
   *
   * @param psuedoVarRecipient The recipient of the psuedo vars.
   *
   * @param paths A vector of paths that hold ExpressionOwner objects,
   *              which must yield LocationPathIterators.
   */
  protected void eleminateSharedPartialPaths(ElemTemplateElement psuedoVarRecipient, Vector paths)
  {
  	MultistepExprHolder list = createMultistepExprList(paths);
  	if(null != list)
  	{
  		if(DIAGNOSE_MULTISTEPLIST)
        	list.diagnose();

        boolean isGlobal = (paths == m_absPaths);

        int longestStepsCount = list.m_stepCount;
    	for (int i = longestStepsCount-1; i >= 1; i--)
    	{
    		MultistepExprHolder next = list;
        	while(null != next)
        	{
        		if(next.m_stepCount < i)
        			break;
				list = matchAndEliminatePartialPaths(next, list, isGlobal, i, psuedoVarRecipient);
				next = next.m_next;
        	}
    	}
  	}
  }

  /**
   * For a given path, see if there are any partitial matches in the list,
   * and, if there are, replace those partial paths with psuedo variable refs,
   * and create the psuedo variable decl.
   *
   * @return The head of the list, which may have changed.
   */
  protected MultistepExprHolder matchAndEliminatePartialPaths(MultistepExprHolder testee,
                                               MultistepExprHolder head,
                                               boolean isGlobal,
                                               int lengthToTest,
                                               ElemTemplateElement varScope)
  {
  	if(null == testee.m_exprOwner)
  		return head;

    WalkingIterator iter1 = (WalkingIterator) testee.m_exprOwner.getExpression();
    if(partialIsVariable(testee, lengthToTest))
    	return head;
    MultistepExprHolder matchedPaths = null;
    MultistepExprHolder matchedPathsTail = null;
    MultistepExprHolder meh = head;
    while( null != meh)
    {
      if ((meh != testee) && (null != meh.m_exprOwner))
      {
	      WalkingIterator iter2 = (WalkingIterator) meh.m_exprOwner.getExpression();
	      if (stepsEqual(iter1, iter2, lengthToTest))
	      {
	        if (null == matchedPaths)
	        {
	          try
	          {
	          	matchedPaths = (MultistepExprHolder)testee.clone();
	          }
	          catch(CloneNotSupportedException cnse){}
	          matchedPathsTail = matchedPaths;
	          matchedPathsTail.m_next = null;
	        }

	        try
	        {
	          matchedPathsTail.m_next = (MultistepExprHolder)meh.clone();
	        }
	        catch(CloneNotSupportedException cnse){}
	        matchedPathsTail = matchedPathsTail.m_next;
	        matchedPathsTail.m_next = null;
	      }
      }
      meh = meh.m_next;
    }

	int matchCount = 0;
	if(null != matchedPaths)
	{
		ElemTemplateElement root = isGlobal ? varScope : findCommonAncestor(matchedPaths);
		WalkingIterator sharedIter = (WalkingIterator)matchedPaths.m_exprOwner.getExpression();
		WalkingIterator newIter = createIteratorFromSteps(sharedIter, lengthToTest);
		ElemVariable var = createPseudoVarDecl(root, newIter, isGlobal);
		if(DIAGNOSE_MULTISTEPLIST)
			System.err.println("Created var: "+var.getName()+(isGlobal ? "(Global)" : ""));
		while(null != matchedPaths)
		{
			ExpressionOwner owner = matchedPaths.m_exprOwner;
			WalkingIterator iter = (WalkingIterator)owner.getExpression();

			if(DIAGNOSE_MULTISTEPLIST)
				diagnoseLineNumber(iter);

			LocPathIterator newIter2 =
			    changePartToRef(var.getName(), iter, lengthToTest, isGlobal);
			owner.setExpression(newIter2);

			matchedPaths = matchedPaths.m_next;
		}
	}

	if(DIAGNOSE_MULTISTEPLIST)
		diagnoseMultistepList(matchCount, lengthToTest, isGlobal);
    return head;
  }

  /**
   * Check if results of partial reduction will just be a variable, in
   * which case, skip it.
   */
  boolean partialIsVariable(MultistepExprHolder testee, int lengthToTest)
  {
  	if(1 == lengthToTest)
  	{
  		WalkingIterator wi = (WalkingIterator)testee.m_exprOwner.getExpression();
  		if(wi.getFirstWalker() instanceof FilterExprWalker)
  			return true;
  	}
  	return false;
  }

  /**
   * Tell what line number belongs to a given expression.
   */
  protected void diagnoseLineNumber(Expression expr)
  {
    ElemTemplateElement e = getElemFromExpression(expr);
    System.err.println("   " + e.getSystemId() + " Line " + e.getLineNumber());
  }

  /**
   * Given a linked list of expressions, find the common ancestor that is
   * suitable for holding a psuedo variable for shared access.
   */
  protected ElemTemplateElement findCommonAncestor(MultistepExprHolder head)
  {
  	int numExprs = head.getLength();
    ElemTemplateElement[] elems = new ElemTemplateElement[numExprs];
    int[] ancestorCounts = new int[numExprs];

  	MultistepExprHolder next = head;
  	int shortestAncestorCount = 10000;
  	for(int i = 0; i < numExprs; i++)
  	{
  		ElemTemplateElement elem =
  			getElemFromExpression(next.m_exprOwner.getExpression());
  		elems[i] = elem;
  		int numAncestors = countAncestors(elem);
  		ancestorCounts[i] = numAncestors;
  		if(numAncestors < shortestAncestorCount)
  		{
  			shortestAncestorCount = numAncestors;
  		}
  		next = next.m_next;
  	}

  	for(int i = 0; i < numExprs; i++)
  	{
  		if(ancestorCounts[i] > shortestAncestorCount)
  		{
  			int numStepCorrection = ancestorCounts[i] - shortestAncestorCount;
  			for(int j = 0; j < numStepCorrection; j++)
  			{
  				elems[i] = elems[i].getParentElem();
  			}
  		}
  	}

  	ElemTemplateElement first = null;
  	while(shortestAncestorCount-- >= 0)
  	{
  		boolean areEqual = true;
  		first = elems[0];
  		for(int i = 1; i < numExprs; i++)
  		{
  			if(first != elems[i])
  			{
  				areEqual = false;
  				break;
  			}
  		}
  		if(areEqual && isNotSameAsOwner(head, first) && first.canAcceptVariables())
  		{
  			if(DIAGNOSE_MULTISTEPLIST)
  			{
  				System.err.print(first.getClass().getName());
  				System.err.println(" at   " + first.getSystemId() + " Line " + first.getLineNumber());
  			}
  			return first;
  		}

  		for(int i = 0; i < numExprs; i++)
  		{
  			elems[i] = elems[i].getParentElem();
  		}
  	}

  	assertion(false, "Could not find common ancestor!!!");
  	return null;
  }

  /**
   * Find out if the given ElemTemplateElement is not the same as one of
   * the ElemTemplateElement owners of the expressions.
   *
   * @param head Head of linked list of expression owners.
   * @param ete The ElemTemplateElement that is a candidate for a psuedo
   * variable parent.
   * @return true if the given ElemTemplateElement is not the same as one of
   * the ElemTemplateElement owners of the expressions.  This is to make sure
   * we find an ElemTemplateElement that is in a viable position to hold
   * psuedo variables that are visible to the references.
   */
  protected boolean isNotSameAsOwner(MultistepExprHolder head, ElemTemplateElement ete)
  {
  	MultistepExprHolder next = head;
  	while(null != next)
  	{
  		ElemTemplateElement elemOwner = getElemFromExpression(next.m_exprOwner.getExpression());
  		if(elemOwner == ete)
  			return false;
  		next = next.m_next;
  	}
  	return true;
  }

  /**
   * Count the number of ancestors that a ElemTemplateElement has.
   *
   * @param elem An representation of an element in an XSLT stylesheet.
   * @return The number of ancestors of elem (including the element itself).
   */
  protected int countAncestors(ElemTemplateElement elem)
  {
  	int count = 0;
  	while(null != elem)
  	{
  		count++;
  		elem = elem.getParentElem();
  	}
  	return count;
  }

  /**
   * Print out diagnostics about partial multistep evaluation.
   */
  protected void diagnoseMultistepList(
      int matchCount,
      int lengthToTest,
      boolean isGlobal)
  {
      if (matchCount > 0)
        {
        System.err.print(
          "Found multistep matches: " + matchCount + ", " + lengthToTest + " length");
        if (isGlobal)
              System.err.println(" (global)");
        else
              System.err.println();
      }
  }

  /**
   * Change a given number of steps to a single variable reference.
   *
   * @param uniquePseudoVarName The name of the variable reference.
   * @param wi The walking iterator that is to be changed.
   * @param numSteps The number of steps to be changed.
   * @param isGlobal true if this will be a global reference.
   */
  protected LocPathIterator changePartToRef(final QName uniquePseudoVarName, WalkingIterator wi,
                                 final int numSteps, final boolean isGlobal)
  {
  	Variable var = new Variable();
  	var.setQName(uniquePseudoVarName);
  	var.setIsGlobal(isGlobal);
  	if(isGlobal)
  	{	ElemTemplateElement elem = getElemFromExpression(wi);
  		StylesheetRoot root = elem.getStylesheetRoot();
  		Vector vars = root.getVariablesAndParamsComposed();
  		var.setIndex(vars.size()-1);
  	}

  	AxesWalker walker = wi.getFirstWalker();
  	for(int i = 0; i < numSteps; i++)
  	{
  		assertion(null != walker, "Walker should not be null!");
  		walker = walker.getNextWalker();
  	}

  	if(null != walker)
  	{

  	  FilterExprWalker few = new FilterExprWalker(wi);
  	  few.setInnerExpression(var);
  	  few.exprSetParent(wi);
  	  few.setNextWalker(walker);
  	  walker.setPrevWalker(few);
  	  wi.setFirstWalker(few);
  	  return wi;
  	}
  	else
  	{
  	  FilterExprIteratorSimple feis = new FilterExprIteratorSimple(var);
  	  feis.exprSetParent(wi.exprGetParent());
  	  return feis;
  	}
  }

  /**
   * Create a new WalkingIterator from the steps in another WalkingIterator.
   *
   * @param wi The iterator from where the steps will be taken.
   * @param numSteps The number of steps from the first to copy into the new
   *                 iterator.
   * @return The new iterator.
   */
  protected WalkingIterator createIteratorFromSteps(final WalkingIterator wi, int numSteps)
  {
  	WalkingIterator newIter = new WalkingIterator(wi.getPrefixResolver());
  	try
  	{
  		AxesWalker walker = (AxesWalker)wi.getFirstWalker().clone();
  		newIter.setFirstWalker(walker);
  		walker.setLocPathIterator(newIter);
  		for(int i = 1; i < numSteps; i++)
  		{
  			AxesWalker next = (AxesWalker)walker.getNextWalker().clone();
  			walker.setNextWalker(next);
  			next.setLocPathIterator(newIter);
  			walker = next;
  		}
  		walker.setNextWalker(null);
  	}
  	catch(CloneNotSupportedException cnse)
  	{
  		throw new WrappedRuntimeException(cnse);
  	}
  	return newIter;
  }

  /**
   * Compare a given number of steps between two iterators, to see if they are equal.
   *
   * @param iter1 The first iterator to compare.
   * @param iter2 The second iterator to compare.
   * @param numSteps The number of steps to compare.
   * @return true If the given number of steps are equal.
   *
   */
  protected boolean stepsEqual(WalkingIterator iter1, WalkingIterator iter2,
                                         int numSteps)
  {
  	AxesWalker aw1 = iter1.getFirstWalker();
  	AxesWalker aw2 = iter2.getFirstWalker();

  	for(int i = 0; (i < numSteps); i++)
  	{
  		if((null == aw1) || (null == aw2))
  		 	return false;

  		if(!aw1.deepEquals(aw2))
  			return false;

  		aw1 = aw1.getNextWalker();
  		aw2 = aw2.getNextWalker();
  	}

  	assertion((null != aw1) || (null != aw2), "Total match is incorrect!");

  	return true;
  }

  /**
   * For the reduction of location path parts, create a list of all
   * the multistep paths with more than one step, sorted by the
   * number of steps, with the most steps occuring earlier in the list.
   * If the list is only one member, don't bother returning it.
   *
   * @param paths Vector of ExpressionOwner objects, which may contain null entries.
   *              The ExpressionOwner objects must own LocPathIterator objects.
   * @return null if no multipart paths are found or the list is only of length 1,
   * otherwise the first MultistepExprHolder in a linked list of these objects.
   */
  protected MultistepExprHolder createMultistepExprList(Vector paths)
  {
  	MultistepExprHolder first = null;
  	int n = paths.size();
  	for(int i = 0; i < n; i++)
  	{
  		ExpressionOwner eo = (ExpressionOwner)paths.elementAt(i);
  		if(null == eo)
  			continue;

  		LocPathIterator lpi = (LocPathIterator)eo.getExpression();
  		int numPaths = countSteps(lpi);
  		if(numPaths > 1)
  		{
  			if(null == first)
  				first = new MultistepExprHolder(eo, numPaths, null);
  			else
  				first = first.addInSortedOrder(eo, numPaths);
  		}
  	}

  	if((null == first) || (first.getLength() <= 1))
  		return null;
  	else
  		return first;
  }

  /**
   * Look through the vector from start point, looking for redundant occurances.
   * When one or more are found, create a psuedo variable declaration, insert
   * it into the stylesheet, and replace the occurance with a reference to
   * the psuedo variable.  When a redundent variable is found, it's slot in
   * the vector will be replaced by null.
   *
   * @param start The position to start looking in the vector.
   * @param firstOccuranceIndex The position of firstOccuranceOwner.
   * @param firstOccuranceOwner The owner of the expression we are looking for.
   * @param psuedoVarRecipient Where to put the psuedo variables.
   *
   * @return The number of expression occurances that were modified.
   */
  protected int findAndEliminateRedundant(int start, int firstOccuranceIndex,
                         ExpressionOwner firstOccuranceOwner,
                         ElemTemplateElement psuedoVarRecipient,
                         Vector paths)
                 throws org.w3c.dom.DOMException
  {
	MultistepExprHolder head = null;
	MultistepExprHolder tail = null;
	int numPathsFound = 0;
	int n = paths.size();

	Expression expr1 = firstOccuranceOwner.getExpression();
	if(DEBUG)
		assertIsLocPathIterator(expr1, firstOccuranceOwner);
	boolean isGlobal = (paths == m_absPaths);
	LocPathIterator lpi = (LocPathIterator)expr1;
	int stepCount = countSteps(lpi);
	for(int j = start; j < n; j++)
	{
		ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
		if(null != owner2)
		{
			Expression expr2 = owner2.getExpression();
			boolean isEqual = expr2.deepEquals(lpi);
			if(isEqual)
			{
				LocPathIterator lpi2  = (LocPathIterator)expr2;
				if(null == head)
				{
					head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
					tail = head;
					numPathsFound++;
				}
				tail.m_next = new MultistepExprHolder(owner2, stepCount, null);
				tail = tail.m_next;

				paths.setElementAt(null, j);

				numPathsFound++;
			}
		}
	}

	if((0 == numPathsFound) && isGlobal)
	{
      head = new MultistepExprHolder(firstOccuranceOwner, stepCount, null);
      numPathsFound++;
	}

	if(null != head)
	{
		ElemTemplateElement root = isGlobal ? psuedoVarRecipient : findCommonAncestor(head);
		LocPathIterator sharedIter = (LocPathIterator)head.m_exprOwner.getExpression();
		ElemVariable var = createPseudoVarDecl(root, sharedIter, isGlobal);
		if(DIAGNOSE_MULTISTEPLIST)
			System.err.println("Created var: "+var.getName()+(isGlobal ? "(Global)" : ""));
		QName uniquePseudoVarName = var.getName();
		while(null != head)
		{
			ExpressionOwner owner = head.m_exprOwner;
			if(DIAGNOSE_MULTISTEPLIST)
				diagnoseLineNumber(owner.getExpression());
			changeToVarRef(uniquePseudoVarName, owner, paths, root);
			head = head.m_next;
		}
		paths.setElementAt(var.getSelect(), firstOccuranceIndex);
	}

	return numPathsFound;
  }

  /**
   * To be removed.
   */
  protected int oldFindAndEliminateRedundant(int start, int firstOccuranceIndex,
                         ExpressionOwner firstOccuranceOwner,
                         ElemTemplateElement psuedoVarRecipient,
                         Vector paths)
                 throws org.w3c.dom.DOMException
  {
	QName uniquePseudoVarName = null;
	boolean foundFirst = false;
	int numPathsFound = 0;
	int n = paths.size();
	Expression expr1 = firstOccuranceOwner.getExpression();
	if(DEBUG)
		assertIsLocPathIterator(expr1, firstOccuranceOwner);
	boolean isGlobal = (paths == m_absPaths);
	LocPathIterator lpi = (LocPathIterator)expr1;
	for(int j = start; j < n; j++)
	{
		ExpressionOwner owner2 = (ExpressionOwner)paths.elementAt(j);
		if(null != owner2)
		{
			Expression expr2 = owner2.getExpression();
			boolean isEqual = expr2.deepEquals(lpi);
			if(isEqual)
			{
				LocPathIterator lpi2  = (LocPathIterator)expr2;
				if(!foundFirst)
				{
					foundFirst = true;
				    ElemVariable var = createPseudoVarDecl(psuedoVarRecipient, lpi, isGlobal);
				    if(null == var)
				    	return 0;
				    uniquePseudoVarName = var.getName();

					changeToVarRef(uniquePseudoVarName, firstOccuranceOwner,
					               paths, psuedoVarRecipient);

					paths.setElementAt(var.getSelect(), firstOccuranceIndex);
					numPathsFound++;
				}

				changeToVarRef(uniquePseudoVarName, owner2, paths, psuedoVarRecipient);

				paths.setElementAt(null, j);

				numPathsFound++;
			}
		}
	}

	if((0 == numPathsFound) && (paths == m_absPaths))
	{
      ElemVariable var = createPseudoVarDecl(psuedoVarRecipient, lpi, true);
      if(null == var)
        return 0;
	  uniquePseudoVarName = var.getName();
      changeToVarRef(uniquePseudoVarName, firstOccuranceOwner, paths, psuedoVarRecipient);
      paths.setElementAt(var.getSelect(), firstOccuranceIndex);
      numPathsFound++;
	}
	return numPathsFound;
  }

  /**
   * Count the steps in a given location path.
   *
   * @param lpi The location path iterator that owns the steps.
   * @return The number of steps in the given location path.
   */
  protected int countSteps(LocPathIterator lpi)
  {
  	if(lpi instanceof WalkingIterator)
  	{
  		WalkingIterator wi = (WalkingIterator)lpi;
  		AxesWalker aw = wi.getFirstWalker();
  		int count = 0;
  		while(null != aw)
  		{
  			count++;
  			aw = aw.getNextWalker();
  		}
  		return count;
  	}
  	else
  		return 1;
  }

  /**
   * Change the expression owned by the owner argument to a variable reference
   * of the given name.
   *
   * Warning: For global vars, this function relies on the variable declaration
   * to which it refers having been added just prior to this function being called,
   * so that the reference index can be determined from the size of the global variables
   * list minus one.
   *
   * @param varName The name of the variable which will be referenced.
   * @param owner The owner of the expression which will be replaced by a variable ref.
   * @param paths The paths list that the iterator came from, mainly to determine
   *              if this is a local or global reduction.
   * @param psuedoVarRecipient The element within whose scope the variable is
   *                           being inserted, possibly a StylesheetRoot.
   */
  protected void changeToVarRef(QName varName, ExpressionOwner owner,
                                Vector paths, ElemTemplateElement psuedoVarRecipient)
  {
	Variable varRef = (paths == m_absPaths) ? new VariableSafeAbsRef() : new Variable();
	varRef.setQName(varName);
	if(paths == m_absPaths)
	{
		StylesheetRoot root = (StylesheetRoot)psuedoVarRecipient;
		Vector globalVars = root.getVariablesAndParamsComposed();
		varRef.setIndex(globalVars.size()-1);
		varRef.setIsGlobal(true);
	}
	owner.setExpression(varRef);
  }

  private synchronized static int getPseudoVarID(){
      return m_uniquePseudoVarID++;
  }

  /**
   * Create a psuedo variable reference that will represent the
   * shared redundent XPath, and add it to the stylesheet.
   *
   * @param psuedoVarRecipient The broadest scope of where the variable
   * should be inserted, usually an xsl:template or xsl:for-each.
   * @param lpi The LocationPathIterator that the variable should represent.
   * @param isGlobal true if the paths are global.
   * @return The new psuedo var element.
   */
  protected ElemVariable createPseudoVarDecl(
      ElemTemplateElement psuedoVarRecipient,
      LocPathIterator lpi, boolean isGlobal)
      throws org.w3c.dom.DOMException
  {
    QName uniquePseudoVarName = new QName (PSUEDOVARNAMESPACE, "#"+getPseudoVarID());

  	if(isGlobal)
  	{
  	  return createGlobalPseudoVarDecl(uniquePseudoVarName,
  	                                  (StylesheetRoot)psuedoVarRecipient, lpi);
  	}
  	else
      return createLocalPseudoVarDecl(uniquePseudoVarName, psuedoVarRecipient, lpi);
  }

  /**
   * Create a psuedo variable reference that will represent the
   * shared redundent XPath, for a local reduction.
   *
   * @param uniquePseudoVarName The name of the new variable.
   * @param stylesheetRoot The broadest scope of where the variable
   *        should be inserted, which must be a StylesheetRoot element in this case.
   * @param lpi The LocationPathIterator that the variable should represent.
   * @return null if the decl was not created, otherwise the new Pseudo var
   *              element.
   */
  protected ElemVariable createGlobalPseudoVarDecl(QName uniquePseudoVarName,
                                           StylesheetRoot stylesheetRoot,
                                           LocPathIterator lpi)
        throws org.w3c.dom.DOMException
  {
  	ElemVariable psuedoVar = new ElemVariable();
  	psuedoVar.setIsTopLevel(true);
	XPath xpath = new XPath(lpi);
	psuedoVar.setSelect(xpath);
	psuedoVar.setName(uniquePseudoVarName);

	Vector globalVars = stylesheetRoot.getVariablesAndParamsComposed();
	psuedoVar.setIndex(globalVars.size());
	globalVars.addElement(psuedoVar);
	return psuedoVar;
  }

  /**
   * Create a psuedo variable reference that will represent the
   * shared redundent XPath, for a local reduction.
   *
   * @param uniquePseudoVarName The name of the new variable.
   * @param psuedoVarRecipient The broadest scope of where the variable
   * should be inserted, usually an xsl:template or xsl:for-each.
   * @param lpi The LocationPathIterator that the variable should represent.
   * @return null if the decl was not created, otherwise the new Pseudo var
   *              element.
   */
  protected ElemVariable createLocalPseudoVarDecl(QName uniquePseudoVarName,
                                           ElemTemplateElement psuedoVarRecipient,
                                           LocPathIterator lpi)
        throws org.w3c.dom.DOMException
  {
		ElemVariable psuedoVar = new ElemVariablePsuedo();

		XPath xpath = new XPath(lpi);
		psuedoVar.setSelect(xpath);
		psuedoVar.setName(uniquePseudoVarName);

		ElemVariable var = addVarDeclToElem(psuedoVarRecipient, lpi, psuedoVar);

		lpi.exprSetParent(var);

		return var;
  }

  /**
   * Add the given variable to the psuedoVarRecipient.
   */
  protected ElemVariable addVarDeclToElem(
    ElemTemplateElement psuedoVarRecipient,
    LocPathIterator lpi,
    ElemVariable psuedoVar)
    throws org.w3c.dom.DOMException
  {
    ElemTemplateElement ete = psuedoVarRecipient.getFirstChildElem();

    lpi.callVisitors(null, m_varNameCollector);

    if (m_varNameCollector.getVarCount() > 0)
    {
      ElemTemplateElement baseElem = getElemFromExpression(lpi);
      ElemVariable varElem = getPrevVariableElem(baseElem);
      while (null != varElem)
      {
        if (m_varNameCollector.doesOccur(varElem.getName()))
          {
          psuedoVarRecipient = varElem.getParentElem();
          ete = varElem.getNextSiblingElem();
          break;
        }
        varElem = getPrevVariableElem(varElem);
      }
    }

    if ((null != ete) && (Constants.ELEMNAME_PARAMVARIABLE == ete.getXSLToken()))
    {
      if(isParam(lpi))
        return null;

      while (null != ete)
      {
        ete = ete.getNextSiblingElem();
        if ((null != ete) && Constants.ELEMNAME_PARAMVARIABLE != ete.getXSLToken())
            break;
      }
    }
    psuedoVarRecipient.insertBefore(psuedoVar, ete);
    m_varNameCollector.reset();
    return psuedoVar;
  }

  /**
   * Tell if the expr param is contained within an xsl:param.
   */
  protected boolean isParam(ExpressionNode expr)
  {
  	while(null != expr)
  	{
  		if(expr instanceof ElemTemplateElement)
  			break;
  		expr = expr.exprGetParent();
  	}
  	if(null != expr)
  	{
  		ElemTemplateElement ete = (ElemTemplateElement)expr;
  		while(null != ete)
  		{
  			int type = ete.getXSLToken();
  			switch(type)
  			{
  				case Constants.ELEMNAME_PARAMVARIABLE:
  					return true;
  				case Constants.ELEMNAME_TEMPLATE:
  				case Constants.ELEMNAME_STYLESHEET:
  					return false;
  			}
  			ete = ete.getParentElem();
  		}
  	}
  	return false;

  }

  /**
   * Find the previous occurance of a xsl:variable.  Stop
   * the search when a xsl:for-each, xsl:template, or xsl:stylesheet is
   * encountered.
   *
   * @param elem Should be non-null template element.
   * @return The first previous occurance of an xsl:variable or xsl:param,
   * or null if none is found.
   */
  protected ElemVariable getPrevVariableElem(ElemTemplateElement elem)
  {
  	while(null != (elem = getPrevElementWithinContext(elem)))
  	{
  		int type = elem.getXSLToken();

  		if((Constants.ELEMNAME_VARIABLE == type) ||
  		   (Constants.ELEMNAME_PARAMVARIABLE == type))
  		{
  			return (ElemVariable)elem;
  		}
  	}
  	return null;
  }

  /**
   * Get the previous sibling or parent of the given template, stopping at
   * xsl:for-each, xsl:template, or xsl:stylesheet.
   *
   * @param elem Should be non-null template element.
   * @return previous sibling or parent, or null if previous is xsl:for-each,
   * xsl:template, or xsl:stylesheet.
   */
  protected ElemTemplateElement getPrevElementWithinContext(ElemTemplateElement elem)
  {
  	ElemTemplateElement prev = elem.getPreviousSiblingElem();
  	if(null == prev)
  		prev = elem.getParentElem();
  	if(null != prev)
  	{
  	  int type = prev.getXSLToken();
  	  if((Constants.ELEMNAME_FOREACH == type) ||
  	     (Constants.ELEMNAME_TEMPLATE == type) ||
  	     (Constants.ELEMNAME_STYLESHEET == type))
  	  {
  	  	prev = null;
  	  }
  	}
  	return prev;
  }

  /**
   * From an XPath expression component, get the ElemTemplateElement
   * owner.
   *
   * @param expr Should be static expression with proper parentage.
   * @return Valid ElemTemplateElement, or throw a runtime exception
   * if it is not found.
   */
  protected ElemTemplateElement getElemFromExpression(Expression expr)
  {
  	ExpressionNode parent = expr.exprGetParent();
  	while(null != parent)
  	{
  		if(parent instanceof ElemTemplateElement)
  			return (ElemTemplateElement)parent;
  		parent = parent.exprGetParent();
  	}
  	throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_NO_TEMPLATE_PARENT, null));
  }

  /**
   * Tell if the given LocPathIterator is relative to an absolute path, i.e.
   * in not dependent on the context.
   *
   * @return true if the LocPathIterator is not dependent on the context node.
   */
  public boolean isAbsolute(LocPathIterator path)
  {
  	int analysis = path.getAnalysisBits();
    boolean isAbs = (WalkerFactory.isSet(analysis, WalkerFactory.BIT_ROOT) ||
           WalkerFactory.isSet(analysis, WalkerFactory.BIT_ANY_DESCENDANT_FROM_ROOT));
    if(isAbs)
    {
    	isAbs = m_absPathChecker.checkAbsolute(path);
    }
    return isAbs;
  }

  /**
   * Visit a LocationPath.
   * @param owner The owner of the expression, to which the expression can
   *              be reset if rewriting takes place.
   * @param path The LocationPath object.
   * @return true if the sub expressions should be traversed.
   */
  public boolean visitLocationPath(ExpressionOwner owner, LocPathIterator path)
  {
  	if(path instanceof SelfIteratorNoPredicate)
  	{
  		return true;
  	}
  	else if(path instanceof WalkingIterator)
  	{
  		WalkingIterator wi = (WalkingIterator)path;
  		AxesWalker aw = wi.getFirstWalker();
  		if((aw instanceof FilterExprWalker) && (null == aw.getNextWalker()))
  		{
  			FilterExprWalker few = (FilterExprWalker)aw;
  			Expression exp = few.getInnerExpression();
  			if(exp instanceof Variable)
  				return true;
  		}
  	}

    if (isAbsolute(path) && (null != m_absPaths))
    {
      if(DEBUG)
        validateNewAddition(m_absPaths, owner, path);
      m_absPaths.addElement(owner);
    }
    else if (m_isSameContext && (null != m_paths))
    {
      if(DEBUG)
        validateNewAddition(m_paths, owner, path);
      m_paths.addElement(owner);
    }

    return true;
  }

  /**
   * Visit a predicate within a location path.  Note that there isn't a
   * proper unique component for predicates, and that the expression will
   * be called also for whatever type Expression is.
   *
   * @param owner The owner of the expression, to which the expression can
   *              be reset if rewriting takes place.
   * @param pred The predicate object.
   * @return true if the sub expressions should be traversed.
   */
  public boolean visitPredicate(ExpressionOwner owner, Expression pred)
  {
    boolean savedIsSame = m_isSameContext;
    m_isSameContext = false;

    pred.callVisitors(owner, this);

    m_isSameContext = savedIsSame;

    return false;
  }

  /**
   * Visit an XSLT top-level instruction.
   *
   * @param elem The xsl instruction element object.
   * @return true if the sub expressions should be traversed.
   */
   public boolean visitTopLevelInstruction(ElemTemplateElement elem)
   {
     int type = elem.getXSLToken();
     switch(type)
     {
       case Constants.ELEMNAME_TEMPLATE :
         return visitInstruction(elem);
       default:
         return true;
     }
   }

  /**
   * Visit an XSLT instruction.  Any element that isn't called by one
   * of the other visit methods, will be called by this method.
   *
   * @param elem The xsl instruction element object.
   * @return true if the sub expressions should be traversed.
   */
  public boolean visitInstruction(ElemTemplateElement elem)
  {
    int type = elem.getXSLToken();
    switch (type)
    {
      case Constants.ELEMNAME_CALLTEMPLATE :
      case Constants.ELEMNAME_TEMPLATE :
      case Constants.ELEMNAME_FOREACH :
        {

          if(type == Constants.ELEMNAME_FOREACH)
          {
            ElemForEach efe = (ElemForEach) elem;

  		    Expression select = efe.getSelect();
  		    select.callVisitors(efe, this);
          }

  		  Vector savedPaths = m_paths;
  		  m_paths = new Vector();

  		  elem.callChildVisitors(this, false);
  		  eleminateRedundentLocals(elem);

  		  m_paths = savedPaths;

          return false;
        }
      case Constants.ELEMNAME_NUMBER :
      case Constants.ELEMNAME_SORT :
        boolean savedIsSame = m_isSameContext;
        m_isSameContext = false;
        elem.callChildVisitors(this);
        m_isSameContext = savedIsSame;
        return false;

      default :
        return true;
    }
  }

  /**
   * Print out to std err the number of paths reduced.
   */
  protected void diagnoseNumPaths(Vector paths, int numPathsEliminated,
                                  int numUniquePathsEliminated)
  {
		if (numPathsEliminated > 0)
		{
		  if(paths == m_paths)
		  {
		    System.err.println("Eliminated " + numPathsEliminated + " total paths!");
		    System.err.println(
		      "Consolodated " + numUniquePathsEliminated + " redundent paths!");
		  }
		  else
		  {
		    System.err.println("Eliminated " + numPathsEliminated + " total global paths!");
		    System.err.println(
		      "Consolodated " + numUniquePathsEliminated + " redundent global paths!");
		  }
		}
  }

  /**
   * Assert that the expression is a LocPathIterator, and, if
   * not, try to give some diagnostic info.
   */
  private final void assertIsLocPathIterator(Expression expr1, ExpressionOwner eo)
    throws RuntimeException
  {
		if(!(expr1 instanceof LocPathIterator))
		{
			String errMsg;
			if(expr1 instanceof Variable)
			{
				errMsg = "Programmer's assertion: expr1 not an iterator: "+
				          ((Variable)expr1).getQName();
			}
			else
			{
				errMsg = "Programmer's assertion: expr1 not an iterator: "+
				          expr1.getClass().getName();
			}
			throw new RuntimeException(errMsg + ", "+
				          eo.getClass().getName()+" "+
				          expr1.exprGetParent());
		}
  }

  /**
   * Validate some assumptions about the new LocPathIterator and it's
   * owner and the state of the list.
   */
  private static void validateNewAddition(Vector paths, ExpressionOwner owner,
                                          LocPathIterator path)
		throws RuntimeException
  {
  	assertion(owner.getExpression() == path, "owner.getExpression() != path!!!");
	int n = paths.size();
	for(int i = 0; i < n; i++)
	{
		ExpressionOwner ew = (ExpressionOwner)paths.elementAt(i);
		assertion(ew != owner, "duplicate owner on the list!!!");
		assertion(ew.getExpression() != path, "duplicate expression on the list!!!");
	}
  }

  /**
   * Simple assertion.
   */
  protected static void assertion(boolean b, String msg)
  {
  	if(!b)
  	{
  		throw new RuntimeException(XSLMessages.createMessage(XSLTErrorResources.ER_ASSERT_REDUNDENT_EXPR_ELIMINATOR, new Object[]{msg}));
  	}
  }

  /**
   * Since we want to sort multistep expressions by length, use
   * a linked list with elements of type MultistepExprHolder.
   */
  class MultistepExprHolder implements Cloneable
  {
	final int m_stepCount;
	MultistepExprHolder m_next;

	/**
	 * Clone this object.
	 */
	public Object clone()
		throws CloneNotSupportedException
	{
		return super.clone();
	}

	/**
	 * Create a MultistepExprHolder.
	 *
	 * @param exprOwner the owner of the expression we are holding.
	 *                  It must hold a LocationPathIterator.
	 * @param stepCount The number of steps in the location path.
	 */
  	MultistepExprHolder(ExpressionOwner exprOwner, int stepCount, MultistepExprHolder next)
  	{
  		m_exprOwner = exprOwner;
  		assertion(null != m_exprOwner, "exprOwner can not be null!");
  		m_stepCount = stepCount;
  		m_next = next;
  	}

	/**
	 * Add a new MultistepExprHolder in sorted order in the list.
	 *
	 * @param exprOwner the owner of the expression we are holding.
	 *                  It must hold a LocationPathIterator.
	 * @param stepCount The number of steps in the location path.
	 * @return The new head of the linked list.
	 */
	MultistepExprHolder addInSortedOrder(ExpressionOwner exprOwner, int stepCount)
	{
		MultistepExprHolder first = this;
		MultistepExprHolder next = this;
		MultistepExprHolder prev = null;
		while(null != next)
		{
			if(stepCount >= next.m_stepCount)
			{
				MultistepExprHolder newholder = new MultistepExprHolder(exprOwner, stepCount, next);
				if(null == prev)
					first = newholder;
				else
					prev.m_next = newholder;

				return first;
			}
			prev = next;
			next = next.m_next;
		}

		prev.m_next = new MultistepExprHolder(exprOwner, stepCount, null);
		return first;
	}

	/**
	 * Remove the given element from the list.  'this' should
	 * be the head of the list.  If the item to be removed is not
	 * found, an assertion will be made.
	 *
	 * @param itemToRemove The item to remove from the list.
	 * @return The head of the list, which may have changed if itemToRemove
	 * is the same as this element.  Null if the item to remove is the
	 * only item in the list.
	 */
	MultistepExprHolder unlink(MultistepExprHolder itemToRemove)
	{
		MultistepExprHolder first = this;
		MultistepExprHolder next = this;
		MultistepExprHolder prev = null;
		while(null != next)
		{
			if(next == itemToRemove)
			{
				if(null == prev)
					first = next.m_next;
				else
					prev.m_next = next.m_next;

				next.m_next = null;

				return first;
			}
			prev = next;
			next = next.m_next;
		}

		assertion(false, "unlink failed!!!");
		return null;
	}

	/**
	 * Get the number of linked list items.
	 */
	int getLength()
	{
		int count = 0;
		MultistepExprHolder next = this;
		while(null != next)
		{
			count++;
			next = next.m_next;
		}
		return count;
	}

    /**
     * Print diagnostics out for the multistep list.
     */
    protected void diagnose()
    {
      System.err.print("Found multistep iterators: " + this.getLength() + "  ");
      MultistepExprHolder next = this;
      while (null != next)
      {
        System.err.print("" + next.m_stepCount);
        next = next.m_next;
        if (null != next)
              System.err.print(", ");
      }
      System.err.println();

FUNC_DECL RETURN TRY VAR_DECL PARAM PARAM CATCH LOOP VAR_DECL THROW_RAISE CALL BINARY_OP CALL CALL CALL CALL LITERAL_ANCHOR VAR_DECL CALL ASSIGN CALL CALL LITERAL_ANCHOR CALL CALL CALL CALL

FUNC_DECL IF VAR_DECL PARAM PARAM PARAM RETURN CALL RETURN CALL CALL BINARY_OP CALL LITERAL_ANCHOR

FUNC_DECL RETURN VAR_DECL VAR_DECL VAR_DECL PARAM PARAM PARAM CALL CALL CALL CALL CALL CALL CALL CALL LITERAL_ANCHOR CALL

FUNC_DECL RETURN IF VAR_DECL VAR_DECL PARAM BINARY_OP CALL ASSIGN CALL CALL CALL

FUNC_DECL SWITCH VAR_DECL PARAM RETURN RETURN CALL LITERAL_ANCHOR CALL

FUNC_DECL IF PARAM PARAM THROW_RAISE CALL CALL


final class UnresolvedRef extends VariableRefBase {

    private QName           _variableName = null;
    private VariableRefBase _ref = null;
    private VariableBase    _var = null;
    private Stylesheet      _sheet = null;

    public UnresolvedRef(QName name) {
	super();
	_variableName = name;
	_sheet = getStylesheet();
    }

    public QName getName() {
	return(_variableName);
    }

    private ErrorMsg reportError() {
	ErrorMsg err = new ErrorMsg(ErrorMsg.VARIABLE_UNDEF_ERR,
				    _variableName, this);
	getParser().reportError(Constants.ERROR, err);
	return(err);
    }

    private VariableRefBase resolve(Parser parser, SymbolTable stable) {
	VariableBase ref = parser.lookupVariable(_variableName);
	if (ref == null) ref = (VariableBase)stable.lookupName(_variableName);
	if (ref == null) {
	    reportError();
	    return null;
	}

	if ((_var = findParentVariable()) != null) _var.addDependency(ref);

	if (ref instanceof Variable)
	    return(new VariableRef((Variable)ref));
	else if (ref instanceof Param)
	    return(new ParameterRef((Param)ref));
	else
	    return null;
    }

    public Type typeCheck(SymbolTable stable) throws TypeCheckError {
	if (_ref != null) {
	    final String name = _variableName.toString();
	    ErrorMsg err = new ErrorMsg(ErrorMsg.CIRCULAR_VARIABLE_ERR,
					name, this);
	}
	if ((_ref = resolve(getParser(), stable)) != null) {
	    return (_type = _ref.typeCheck(stable));
	}
	throw new TypeCheckError(reportError());
    }

    public void translate(ClassGenerator classGen, MethodGenerator methodGen) {
	if (_ref != null)
	    _ref.translate(classGen, methodGen);
	else
	    reportError();
    }

    public String toString() {
	return "unresolved-ref()";

FUNC_DECL RETURN VAR_DECL CALL CALL CALL


public class XMLMessageOutInterceptor extends AbstractMessageOutInterceptor<XMLMessage> {
    private static final Logger LOG = LogUtils.getL7dLogger(XMLMessageOutInterceptor.class);

    public XMLMessageOutInterceptor() {
        super(Phase.PREPARE_SEND);
        addAfter(DOMOutInterceptor.class.getName());
    }

    protected Logger getLogger() {
        return LOG;
    }

    @SuppressWarnings("unchecked")
    public void handleMessage(XMLMessage message) throws Fault {
        Exchange exchange = message.getExchange();
        BindingMessageInfo bmi = exchange.get(BindingMessageInfo.class);

        List<Element> payload = message.get(List.class);
        if (bmi == null && payload.size() > 1) {
            throw new Fault(new org.apache.cxf.common.i18n.Message(
                            "NO_XML_ROOT_NODE", LOG));
        }

        if (bmi != null) {
            Element header = message.get(Element.class);
            if (header != null) {
                if (LOG.isLoggable(Level.INFO)) {
                    LOG.info("DOMOutInterceptor Copy Message Part related Headers to Payload.");
                }
                moveHeaderPartToPayload(bmi, header, payload);
            }

            XMLBindingMessageFormat msgFormat =
                bmi.getExtensor(XMLBindingMessageFormat.class);
            QName rootName = msgFormat != null ? msgFormat.getRootNode() : null;

            if (rootName == null) {
                if (payload.size() > 1) {
                    throw new Fault(new org.apache.cxf.common.i18n.Message(
                                    "NO_XML_ROOT_NODE", LOG));
                }
            } else {
                if (LOG.isLoggable(Level.INFO)) {
                    LOG.info("DOMOutInterceptor Create xmlformat RootNode element");
                }
                Element el = createElement(rootName, payload);
                payload = new ArrayList<Element>();
                payload.add(el);
            }

            message.put(List.class, payload);
            message.remove(Element.class);
        }
    }

    private void moveHeaderPartToPayload(BindingMessageInfo bmi,
                                         Element header,
                                         List<Element> payload) {
        Collection<MessagePartInfo> bodyParts = bmi.getMessageParts();
        NodeList nodes = header.getChildNodes();

        for (int idx = 0; idx < nodes.getLength(); idx++) {
            Node node = nodes.item(idx);
            int index = 0;
            for (MessagePartInfo mpi : bodyParts) {
                QName name = mpi.getConcreteName();
                if (name.getLocalPart().equals(node.getLocalName())
                    && name.getNamespaceURI().equals(node.getNamespaceURI())) {
                    payload.add(index, (Element)node);
                    break;
                }
                ++index;


org.gjt.sp.jedit.ServiceListHandler

class ServiceListHandler extends HandlerBase
{
	//{{{ ServiceListHandler constructor
	ServiceListHandler(PluginJAR plugin, URL uri)
	{
		this.plugin = plugin;
		this.uri = uri;
		stateStack = new Stack();
		cachedServices = new LinkedList();
	} //}}}

	//{{{ resolveEntity() method
	public Object resolveEntity(String publicId, String systemId)
	{
		if("services.dtd".equals(systemId))
		{
			// this will result in a slight speed up, since we
			// don't need to read the DTD anyway, as AElfred is
			// non-validating
			return new StringReader("<!-- -->");

			/* try
			{
				return new BufferedReader(new InputStreamReader(
					getClass().getResourceAsStream
					("/org/gjt/sp/jedit/services.dtd")));
			}
			catch(Exception e)
			{
				Log.log(Log.ERROR,this,"Error while opening"
					+ " dockables.dtd:");
				Log.log(Log.ERROR,this,e);
			} */
		}

		return null;
	} //}}}

	//{{{ attribute() method
	public void attribute(String aname, String value, boolean isSpecified)
	{
		if(aname.equals("NAME"))
			serviceName = value;
		else if(aname.equals("CLASS"))
			serviceClass = value;
	} //}}}

	//{{{ doctypeDecl() method
	public void doctypeDecl(String name, String publicId,
		String systemId) throws Exception
	{
		if("SERVICES".equals(name))
			return;

		Log.log(Log.ERROR,this,uri + ": DOCTYPE must be SERVICES");
	} //}}}

	//{{{ charData() method
	public void charData(char[] c, int off, int len)
	{
		String tag = peekElement();
		String text = new String(c, off, len);

		if (tag == "SERVICE")
		{
			code = text;
		}
	} //}}}

	//{{{ startElement() method
	public void startElement(String tag)
	{
		tag = pushElement(tag);
	} //}}}

	//{{{ endElement() method
	public void endElement(String name)
	{
		if(name == null)
			return;

		String tag = peekElement();

		if(name.equals(tag))
		{
			if(tag == "SERVICE")
			{
				ServiceManager.Descriptor d =
					new ServiceManager.Descriptor(
					serviceClass,serviceName,code,plugin);
				ServiceManager.registerService(d);
				cachedServices.add(d);
			}

			popElement();
		}
		else
		{
			// can't happen
			throw new InternalError();
		}
	} //}}}

	//{{{ startDocument() method
	public void startDocument()
	{
		try
		{
			pushElement(null);
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	} //}}}

	//{{{ getCachedServices() method
	public ServiceManager.Descriptor[] getCachedServices()
	{
		return (ServiceManager.Descriptor[])cachedServices.toArray(
			new ServiceManager.Descriptor[cachedServices.size()]);
	} //}}}

	//{{{ Private members

	//{{{ Instance variables
	private PluginJAR plugin;
	private URL uri;

	private String serviceName;
	private String serviceClass;
	private String code;

	private Stack stateStack;

	private List cachedServices;
	//}}}

	//{{{ pushElement() method
	private String pushElement(String name)
	{
		name = (name == null) ? null : name.intern();

		stateStack.push(name);

		return name;
	} //}}}

	//{{{ peekElement() method
	private String peekElement()
	{
		return (String) stateStack.peek();
	} //}}}

	//{{{ popElement() method
	private String popElement()
	{
		return (String) stateStack.pop();

FUNC_DECL IF PARAM PARAM PARAM IF CALL CALL ASSIGN LITERAL_ANCHOR ASSIGN LITERAL_ANCHOR

FUNC_DECL TRY CATCH CALL CALL LITERAL_ANCHOR

FUNC_DECL PARAM PARAM ASSIGN ASSIGN ASSIGN ASSIGN CALL CALL