/*
* Copyright 2020 White Magic Software, Ltd.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.scrivenvar.processors;
/**
* Responsible for transforming a document through a variety of chained
* handlers. If there are conditions where this handler should not process the
* entire chain, create a second handler, or split the chain into reusable
* sub-chains.
*
* @param <T> The type of object to process.
*/
public abstract class AbstractProcessor<T> implements Processor<T> {
/**
* Used while processing the entire chain; null to signify no more links.
*/
private final Processor<T> mNext;
/**
* Constructs a new default handler with no successor.
*/
protected AbstractProcessor() {
this( null );
}
/**
* Constructs a new default handler with a given successor.
*
* @param successor The next processor in the chain.
*/
public AbstractProcessor( final Processor<T> successor ) {
mNext = successor;
}
@Override
public Processor<T> next() {
return mNext;
}
/**
* This algorithm is incorrect, but works for the one use case of removing
* the ending HTML Preview Processor from the end of the processor chain.
* The processor chain is immutable so this creates a succession of
* delegators that wrap each processor in the chain, except for the one
* to be removed.
* <p>
* An alternative is to update the {@link ProcessorFactory} with the ability
* to create a processor chain devoid of an {@link HtmlPreviewProcessor}.
* </p>
*
* @param removal The {@link Processor} to remove from the chain.
* @return A delegating processor chain starting from this processor
* onwards with the given processor removed from the chain.
*/
@Override
public Processor<T> remove( final Class<? extends Processor<T>> removal ) {
Processor<T> p = this;
final ProcessorDelegator<T> head = new ProcessorDelegator<>( p );
ProcessorDelegator<T> result = head;
while( p != null ) {
final Processor<T> next = p.next();
if( next != null && next.getClass() != removal ) {
final var delegator = new ProcessorDelegator<>( next );
result.setNext( delegator );
result = delegator;
}
p = p.next();
}
return head;
}
private static final class ProcessorDelegator<T>
extends AbstractProcessor<T> {
private final Processor<T> mDelegate;
private Processor<T> mNext;
public ProcessorDelegator( final Processor<T> delegate ) {
super( delegate );
assert delegate != null;
mDelegate = delegate;
}
@Override
public T process( T t ) {
return mDelegate.process( t );
}
protected void setNext( final Processor<T> next ) {
mNext = next;
}
@Override
public Processor<T> next() {
return mNext;
}
}
}