/** * Warlock, the open-source cross-platform game client * * Copyright 2008, Warlock LLC, and individual contributors as indicated * by the @authors tag. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ /* * Created on Feb 16, 2005 * * TODO To change the template for this generated file go to * Window - Preferences - Java - Code Style - Code Templates */ package cc.warlock.core.stormfront.internal; import java.util.HashMap; import java.util.Stack; import cc.warlock.core.client.IStream; import cc.warlock.core.client.IWarlockStyle; import cc.warlock.core.client.WarlockString; import cc.warlock.core.client.WarlockStringMarker; import cc.warlock.core.stormfront.IStormFrontProtocolHandler; import cc.warlock.core.stormfront.IStormFrontTagHandler; import cc.warlock.core.stormfront.client.IStormFrontClient; import cc.warlock.core.stormfront.xml.StormFrontAttributeList; /** * @author sproctor * * TODO To change the template for this generated type comment go to * Window - Preferences - Java - Code Style - Code Templates */ public class StormFrontProtocolHandler implements IStormFrontProtocolHandler { protected IStormFrontClient client; protected HashMap<String, IStormFrontTagHandler> defaultTagHandlers = new HashMap<String, IStormFrontTagHandler>(); protected TextDest textDest = null; protected Stack<String> tagStack = new Stack<String>(); protected Stack<WarlockStringMarker> styleStack = new Stack<WarlockStringMarker>(); private WarlockString buffer = null; protected int currentSpacing = 0; protected int monsterCount = 0; protected IWarlockStyle boldStyle = null; private boolean lineHasTag = false; private boolean lineHasContent = false; protected interface TextDest { public void put(WarlockString text); public void flush(); } protected class StreamTextDest implements TextDest { private IStream stream; public StreamTextDest(IStream stream) { this.stream = stream; } public void put(WarlockString text) { stream.put(text); } public IStream getStream() { return stream; } public void flush() { } } protected class ComponentTextDest implements TextDest { private WarlockString buffer = new WarlockString(); private String id; public ComponentTextDest(String id) { this.id = id; } public void put(WarlockString text) { buffer.append(text); } public void flush() { client.updateComponent(id, buffer); buffer = null; } } public StormFrontProtocolHandler(IStormFrontClient client) { this.client = client; // server settings handlers new PlayerIDTagHandler(this); new ModeTagHandler(this); new SettingsTagHandler(this); new SettingsInfoTagHandler(this); new CmdtimestampTagHandler(this, new CmdlistTagHandler(this)); new SentSettingsTagHandler(this); // Register the handlers new AppTagHandler(this); new DialogDataTagHandler(this); new PromptTagHandler(this); new RoundtimeTagHandler(this); new CasttimeTagHandler(this); new CompDefTagHandler(this); // for the room stream new NavTagHandler(this); // for nextRoom notification new CompassTagHandler(this); // stream handlers new PushStreamTagHandler(this); new PopStreamTagHandler(this); new ClearStreamTagHandler(this); new StreamTagHandler(this); new StreamWindowTagHandler(this); // Container handlers new ClearContainerTagHandler(this); new InvTagHandler(this); new SpellTagHandler(this); new LeftTagHandler(this); new RightTagHandler(this); new ComponentTagHandler(this); new StyleTagHandler(this); new OutputTagHandler(this); new PushBoldTagHandler(this); new PopBoldTagHandler(this); new PresetTagHandler(this); new IndicatorTagHandler(this); new LaunchURLTagHandler(this); new ResourceTagHandler(this); new ATagHandler(this); new BTagHandler(this); new DTagHandler(this); new StubTagHandler(this); // handles known tags that don't have an implementation. } /* * This function is to register handlers for xml tags */ public void registerHandler(IStormFrontTagHandler tagHandler) { for (String tagName : tagHandler.getTagNames()) { defaultTagHandlers.put(tagName, tagHandler); } } /* * The purpose of this function is painfully obvious. */ public IStormFrontClient getClient() { return client; } /* * push a stream onto the stack */ public void setDestStream(String streamId) { clearDest(); IStream stream = client.getStream(streamId); if(stream != null) textDest = new StreamTextDest(stream); lineHasContent = false; } public void setDestComponent(String id) { clearDest(); textDest = new ComponentTextDest(id); lineHasContent = false; } public void clearDest() { clearStyles(); if(textDest != null) { textDest.flush(); textDest = null; } lineHasContent = false; } public IStream getCurrentStream () { if(textDest == null || !(textDest instanceof StreamTextDest)) return client.getDefaultStream(); else return ((StreamTextDest)textDest).getStream(); } /* (non-Javadoc) * @see org.xml.sax.ContentHandler#characters(char[], int, int) */ public void characters(String characters) { // if there was no handler or it couldn't handle the characters, // take a default action if(!handleCharacters(characters)) { if(styleStack.isEmpty()) { String str = characters; // Suppress newlines following tags when the line is empty if(lineHasTag && !lineHasContent) { if(str.startsWith("\n")) str = str.substring(1); else if(str.startsWith("\r\n")) str = str.substring(2); } if(str.length() > 0) { WarlockString text = new WarlockString(str); if(textDest != null) textDest.put(text); else client.getDefaultStream().put(text); } } else { buffer.append(characters); } if(characters.contains("\n")) lineHasTag = false; // I don't think we need to handle line endings with \n\r or \r lineHasContent = !characters.endsWith("\n"); } } private boolean handleCharacters(String characters) { // Start looking for handlers at the highest level tag for(int pos = tagStack.size() - 1; pos >= 0; pos--) { String tagName = tagStack.get(pos); IStormFrontTagHandler tagHandler = getTagHandlerForElement(tagName); if(tagHandler != null) { tagHandler.setCurrentTag(tagName); // if the handler handled the characters, we're done if(tagHandler.handleCharacters(characters)) return true; } } return false; } /* (non-Javadoc) * @see org.xml.sax.ContentHandler#endElement(java.lang.String, java.lang.String, java.lang.String) */ public void endElement(String name, String rawXML) { // Get the tag name off the stack if(tagStack.size() == 0 || !name.equals(tagStack.peek())) { System.err.println("Unexpected close tag \"" + name + "\"."); } else { tagStack.pop(); } // call the method for the object IStormFrontTagHandler tagHandler = getTagHandlerForElement(name); if(tagHandler != null) { tagHandler.setCurrentTag(name); tagHandler.handleEnd(rawXML); } lineHasTag = true; } /* (non-Javadoc) * @see org.xml.sax.ContentHandler#startElement(java.lang.String, java.lang.String, java.lang.String, org.xml.sax.Attributes) */ public void startElement(String name, StormFrontAttributeList attributes, String rawXML) { // call the method for the object IStormFrontTagHandler tagHandler = getTagHandlerForElement(name); tagStack.push(name); if(tagHandler != null) { tagHandler.setCurrentTag(name); tagHandler.handleStart(attributes, rawXML); } else { System.err.println("Unhandled tag \"" + name + "\". Probably an unsupported tag."); } lineHasTag = true; } private IStormFrontTagHandler getTagHandlerForElement(String name) { if(tagStack.size() == 0) return defaultTagHandlers.get(name); IStormFrontTagHandler rv = getTagHandlerForElementHelper(name, null, 0, false); if(rv == null) rv = defaultTagHandlers.get(name); return rv; } private IStormFrontTagHandler getTagHandlerForElementHelper(String name, IStormFrontTagHandler parentHandler, int stackPosition, boolean origParent) { if(stackPosition < tagStack.size()) { // Walk down the stack until we hit the bottom, then search for our tag out the way back out String tagName = tagStack.get(stackPosition); // next handler is the child the parent IStormFrontTagHandler nextHandler; if(parentHandler != null) nextHandler = parentHandler.getTagHandler(tagName); else nextHandler = defaultTagHandlers.get(tagName); // If we just searched for the tag we're looking for, return the result. if(name.equals(tagName)) return nextHandler; // If we found a handler, then it's original boolean nextIsOrig = nextHandler != null; // If there was no match, use the current parent (not original) if(nextHandler == null) { nextHandler = parentHandler; } // see if there is a valid handler further down the tree. IStormFrontTagHandler tagHandler = getTagHandlerForElementHelper(name, nextHandler, stackPosition + 1, nextIsOrig); if(tagHandler != null) { return tagHandler; } else { if(origParent) return parentHandler.getTagHandler(name); else return null; } } else { if(origParent) return parentHandler.getTagHandler(name); else return null; } } public void addStyle(IWarlockStyle style) { if(buffer == null) buffer = new WarlockString(); WarlockStringMarker marker = new WarlockStringMarker(style, buffer.length(), buffer.length()); if(!styleStack.isEmpty()) { WarlockStringMarker lastStyle = styleStack.peek(); lastStyle.addMarker(marker); } else { buffer.addMarker(marker); } styleStack.push(marker); lineHasContent = true; } public void removeStyle(IWarlockStyle style) { if(styleStack.isEmpty() || styleStack.peek().getStyle() != style) return; WarlockStringMarker marker = styleStack.pop(); marker.setEnd(buffer.length()); if(styleStack.isEmpty()) { flushBuffer(); } } private void flushBuffer() { if(buffer == null) return; if(textDest != null) textDest.put(buffer); else client.getDefaultStream().put(buffer); buffer = null; } public void clearStyles() { boldStyle = null; if(buffer == null) { styleStack.clear(); } else { while(!styleStack.isEmpty()) { WarlockStringMarker marker = styleStack.pop(); marker.setEnd(buffer.length()); } flushBuffer(); } } public void startBold() { if (boldStyle == null) { boldStyle = this.getClient().getClientSettings().getNamedStyle("bold"); this.addStyle(boldStyle); } } public void stopBold() { if (boldStyle != null) { this.removeStyle(boldStyle); boldStyle = null; } } public IStormFrontTagHandler getTagHandler(Class<? extends IStormFrontTagHandler> handlerType) { for (IStormFrontTagHandler handler : defaultTagHandlers.values()) { if (handler.getClass().equals(handlerType)) return handler; } return null; } public void resetMonsterCount() { monsterCount = 0; } public void incrementMonsterCount() { monsterCount++; } public int getMonsterCount() { return monsterCount; } }