/**
*
*/
package org.radeox.filter.balance;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.regex.Matcher;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author andrew
*/
public class Balancer
{
private static final Log log = LogFactory.getLog(Balancer.class);
StringBuffer sb;
TagStack tagStack;
TagStack rememberedStack;
Matcher m;
int head;
public String filter()
{
sb = new StringBuffer();
tagStack = new TagStack();
rememberedStack = new TagStack();
m.reset();
head = -1;
while (m.find())
{
if (m.group(1) != null)
{
tagStack.push(new Tag(m.group(2), m.group(1)));
// We have an open!!
if (rememberedStack.size() > 0 && head < m.start())
{
/*
* We have text between the head and the open tag emit the
* remembered tags:
*/
emitOpenWithRemembered();
}
else
{
emitOpen();
}
}
else
{
if (rememberedStack.size() > 0 && head < m.start())
{
emitCloseWithRemembered();
}
else
{
emitClose();
}
}
head = m.end();
}
m.appendTail(sb);
return sb.toString();
}
private void emitClose()
{
doClosing(new StringBuffer());
}
private void emitCloseWithRemembered()
{
// OPEN the remembered tags
for (Iterator it = rememberedStack.iterator(); it.hasNext();)
{
Tag tag = (Tag) it.next();
sb.append(tag.open);
}
// Create replacement string
StringBuffer replacementBuffer = new StringBuffer();
for (Iterator it = rememberedStack.backIterator(); it.hasNext();)
{
Tag tag = (Tag) it.next();
replacementBuffer.append("</").append(tag.name).append(">");
}
doClosing(replacementBuffer);
}
private void doClosing(StringBuffer replacementBuffer)
{
Tag currentTag = new Tag(m.group(4));
// Now check what we're closing...
if (tagStack.search(currentTag) > -1)
{
/*
* The tagStack contains this current tag we've got: 1. <A> </A> -->
* No work 2. <A> <B> </A> ... --> must remember <B>! 3. <A> <B> <C>
* </A> --> must remember <B> and <C>! 4. <A> <B attrs=""> </A> <A>
* <B other=""> <C> </B> ... --> remember <C> and keep remembering
* <B attrs=""> (Case not dealt by this check: <A> <B> </A> <A> </B>
* ... ) Action is: pop everything off the tagStack, and remember
* them till we hit the currentTag
*/
for (Tag tag = tagStack.pop(); (tagStack.size() > 0) && !currentTag.equals(tag); tag = tagStack.pop())
{
if ( tag != null ) {
replacementBuffer.append("</").append(tag.name).append(">");
rememberedStack.push(tag);
} else {
log.warn("Found Null tag in ballancer ");
}
}
replacementBuffer.append("</").append(currentTag.name).append(">");
}
else
{
/*
* The tag stack doesn't contain the current tag: We are closing a
* remembered tag! (well we hope!) 1. <A> <B> </A> <A> </B> ... -->
* just forget the B 2. <A> <B> <C> </A> <A> </B> ... --> just
* forget the B 3. <A> <B attr=""> </A> <B other=""> <C> </A> <A>
* </B> ... --> forget the <B other=""> not the <B>
*/
TagStack tempStack = new TagStack();
for (Tag tag = rememberedStack.pop(); (rememberedStack.size() > 0) && !currentTag.equals(tag); tag = rememberedStack
.pop())
{
if (tag != null)
{
tempStack.push(tag);
}
}
for (Tag tag = tempStack.pop(); tag != null; tag = tempStack.pop())
{
rememberedStack.push(tag);
}
}
m.appendReplacement(sb, replacementBuffer.toString());
}
public void setMatcher(Matcher matcher)
{
this.m = matcher;
}
private void emitOpenWithRemembered()
{
// Append the opens to sb
for (Iterator it = rememberedStack.iterator(); it.hasNext();)
{
Tag tag = (Tag) it.next();
sb.append(tag.open);
}
// Create replacement string
StringBuffer buffer = new StringBuffer();
for (Iterator it = rememberedStack.backIterator(); it.hasNext();)
{
Tag tag = (Tag) it.next();
buffer.append("</").append(tag.name).append(">");
}
buffer.append(m.group(1).replaceAll("\\\\", "\\\\\\\\").replaceAll("\\$",
"\\\\\\$"));
m.appendReplacement(sb, buffer.toString());
}
private void emitOpen()
{
m.appendReplacement(sb, "$1");
}
class Tag
{
public Tag(String name, String open)
{
this.name = name;
this.open = open;
}
public Tag(String name)
{
this.name = name;
this.open = null;
}
public String name;
public String open;
public boolean equals(Object o)
{
if (o == null) return false;
if (o instanceof Tag)
{
Tag that = (Tag) o;
if (that.name == null && this.name == null) return true;
if (this.name == null) return false;
return (this.name.equals(that.name));
}
return false;
}
}
class TagStack
{
List internalList;
int size = 0;
public TagStack()
{
internalList = new ArrayList();
}
public Tag push(Tag toPush)
{
internalList.add(size++, toPush);
if (size > 1)
{
return (Tag) internalList.get(size - 2);
}
else
{
return null;
}
}
public Tag peek()
{
if (size > 0)
{
return (Tag) internalList.get(size - 1);
}
else
{
return null;
}
}
public Tag pop()
{
if (size > 0)
{
return (Tag) internalList.get(--size);
}
else
{
return null;
}
}
public int size()
{
return size;
}
public boolean empty()
{
return (size == 0);
}
public int search(Tag o)
{
if (o == null)
{
return -1;
}
for (int i = size; i > 0; i--)
{
if (o.equals(internalList.get(i - 1)))
{
return (size - i + 1);
}
}
return -1;
}
public Tag get(int i)
{
if (i < size)
{
return (Tag) internalList.get(i);
}
else
{
throw new ArrayIndexOutOfBoundsException(i);
}
}
public Iterator iterator()
{
return new Iterator()
{
int ourHead = size;
public boolean hasNext()
{
return ourHead > 0;
}
public Object next()
{
return internalList.get(--ourHead);
}
public void remove()
{
throw new UnsupportedOperationException(
"remove is not implemented for this iterator");
}
};
}
public Iterator backIterator()
{
return new Iterator()
{
int ourHead = 0;
public boolean hasNext()
{
return (ourHead < size);
}
public Object next()
{
return internalList.get(ourHead++);
}
public void remove()
{
throw new UnsupportedOperationException(
"remove is not implemented for this iterator");
}
};
}
public String toString()
{
StringBuffer sb = new StringBuffer();
for (Iterator it = iterator(); it.hasNext();)
{
Tag t = (Tag) it.next();
sb.append("Tag: " + t.open + "\n");
}
return sb.toString();
}
}
}