package com.aionemu.packetsamurai.protocol; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; import java.math.BigInteger; import java.util.Collections; import java.util.Comparator; import java.util.List; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javolution.util.FastList; import org.w3c.dom.DOMException; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.ls.DOMImplementationLS; import org.w3c.dom.ls.LSOutput; import org.w3c.dom.ls.LSSerializer; import org.xml.sax.ErrorHandler; import org.xml.sax.SAXException; import org.xml.sax.SAXParseException; import com.aionemu.packetsamurai.PacketSamurai; import com.aionemu.packetsamurai.Util; import com.aionemu.packetsamurai.parser.DataStructure; import com.aionemu.packetsamurai.parser.PartType; import com.aionemu.packetsamurai.parser.PartTypeManager; import com.aionemu.packetsamurai.parser.formattree.ForPart; import com.aionemu.packetsamurai.parser.formattree.Part; import com.aionemu.packetsamurai.parser.formattree.PartContainer; import com.aionemu.packetsamurai.parser.formattree.SwitchCaseBlock; import com.aionemu.packetsamurai.parser.formattree.SwitchPart; import com.aionemu.packetsamurai.parser.parttypes.RawPartType; import com.aionemu.packetsamurai.parser.valuereader.Reader; import com.aionemu.packetsamurai.protocol.protocoltree.PacketFamilly; import com.aionemu.packetsamurai.protocol.protocoltree.PacketFormat; import com.aionemu.packetsamurai.protocol.protocoltree.ProtocolNode; import com.aionemu.packetsamurai.protocol.protocoltree.PacketFamilly.packetDirection; import com.aionemu.packetsamurai.session.DataPacket; import com.sun.org.apache.xerces.internal.impl.Constants; /** * * @author Gilles Duboscq * */ public class Protocol { private PacketFamilly _serverPackets; private PacketFamilly _clientPackets; private int _port; // this can be used to specify a tcp or udp port private int _sizeBytes; // this is the number of bytes used to specify the size of packets (2 is here as a default value that will be changed if specified) private int _checksumSize = 0; private boolean _loaded = false; private boolean _strictLength = false; private boolean _analyzerEnabled = true; private String _encryption; private String _name; private String _filename; private String _packetBufferType = "DefaultPacketBuffer"; private static final Comparator<ProtocolNode> PROTOCOL_NODE_COMPARATOR = new Comparator<ProtocolNode>() { public int compare(ProtocolNode o1, ProtocolNode o2) { return o1.getID() - o2.getID(); } }; public Protocol(String pFile) { _loaded = false; _filename = pFile; loadProtocol(pFile); } public Protocol(int sizeBytes, String name, int port, String encryption) { _sizeBytes = sizeBytes; _name = name; _port = port; _encryption = encryption; _clientPackets = new PacketFamilly("ClientPackets"); _clientPackets.setDirection(packetDirection.clientPacket); _serverPackets = new PacketFamilly("ServerPackets"); _serverPackets.setDirection(packetDirection.serverPacket); } public Protocol(int sizeBytes, String name, int port) { this(sizeBytes, name, port, "Null"); } public void saveProtocol(String fileName) { try { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(true); factory.setIgnoringComments(true); DocumentBuilder docBuilder = factory.newDocumentBuilder(); Document doc = docBuilder.newDocument(); DOMImplementationLS implLS = (DOMImplementationLS) docBuilder.getDOMImplementation(); LSSerializer domWriter = implLS.createLSSerializer(); LSOutput output = implLS.createLSOutput(); domWriter.getDomConfig().setParameter(Constants.DOM_FORMAT_PRETTY_PRINT, Boolean.TRUE); Element root = doc.createElement("protocol"); root.setAttribute("port", Integer.toString(_port)); root.setAttribute("sizebytes", Integer.toString(_sizeBytes)); root.setAttribute("crypt", _encryption); root.setAttribute("strictlength", Boolean.toString(_strictLength)); root.setAttribute("name", _name); root.setAttribute("packetbuffer", _packetBufferType); root.setAttribute("checksumSize", Integer.toString(_checksumSize)); if(_serverPackets != null) { Element serverPacketFamilly = doc.createElement("packetfamilly"); serverPacketFamilly.setAttribute("way","ServerPackets"); serverPacketFamilly.setAttribute("switchtype",_serverPackets.getSwitchType().getName()); // sort elements in family FastList<ProtocolNode> nodes = new FastList<ProtocolNode>(_serverPackets.getNodes().values()); Collections.sort(nodes, PROTOCOL_NODE_COMPARATOR); for(ProtocolNode node : nodes) { buildDOM(node, serverPacketFamilly, doc); } root.appendChild(serverPacketFamilly); } else { PacketSamurai.getUserInterface().log("Server Packets were not loaded, skipped while saving"); } if(_clientPackets != null) { Element clientPacketFamilly = doc.createElement("packetfamilly"); clientPacketFamilly.setAttribute("way","ClientPackets"); clientPacketFamilly.setAttribute("switchtype",_clientPackets.getSwitchType().getName()); // sort elements in family FastList<ProtocolNode> nodes = new FastList<ProtocolNode>(_clientPackets.getNodes().values()); Collections.sort(nodes, PROTOCOL_NODE_COMPARATOR); for(ProtocolNode node : nodes) { buildDOM(node, clientPacketFamilly, doc); } root.appendChild(clientPacketFamilly); } else { PacketSamurai.getUserInterface().log("Client Packets were not loaded, skipped while saving"); } doc.appendChild(root); OutputStream outputStream = new FileOutputStream(new File(fileName)); output.setByteStream(outputStream); output.setEncoding("UTF-8"); domWriter.write(doc, output); outputStream.close(); } catch (Exception e) { e.printStackTrace(); } } public PacketFormat getFormat(DataPacket packet, PacketId _packetid) { if(packet.getDirection() == packetDirection.serverPacket) { if (_serverPackets == null) return null; int i = packet.readIntType(_serverPackets.getSwitchType()); if(_checksumSize != 0) packet.getByteBuffer().position(packet.getByteBuffer().position()+_checksumSize); _packetid.add(i,_serverPackets.getSwitchType()); ProtocolNode node = _serverPackets.getNodes().get(i); while(node instanceof PacketFamilly) { PacketFamilly familly = (PacketFamilly)node; i = packet.readIntType(familly.getSwitchType()); _packetid.add(i,familly.getSwitchType()); node = familly.getNodes().get(i); } return (PacketFormat)node; } else if(packet.getDirection() == packetDirection.clientPacket) { int i = packet.readIntType(_clientPackets.getSwitchType()); if(_checksumSize != 0) packet.getByteBuffer().position(packet.getByteBuffer().position()+_checksumSize); _packetid.add(i,_clientPackets.getSwitchType()); ProtocolNode node = _clientPackets.getNodes().get(i); while(node instanceof PacketFamilly) { PacketFamilly familly = (PacketFamilly)node; i = packet.readIntType(familly.getSwitchType()); _packetid.add(i,familly.getSwitchType()); node = familly.getNodes().get(i); } return (PacketFormat)node; } return null; } public PacketFamilly getServerPacketsFamilly() { return _serverPackets; } public PacketFamilly getClientPacketsFamilly() { return _clientPackets; } public int getPort() { return _port; } public int getSizeBytes() { return _sizeBytes; } public int getChecksumSize() { return _checksumSize; } public void setPort(int port) { _port = port; } public void setSizeBytes(int size) { _sizeBytes = size; } public void setEncryption(String enc) { _encryption = enc; } public String getEncryption() { return _encryption; } public boolean isLoaded() { return _loaded; } public String getName() { return _name; } public void setName(String name) { _name = name; } public String getFileName() { return _filename; } public void saveProtocol() { if(_filename == null) saveProtocol(ProtocolManager.getInstance().getProtocolsDirectory()+"/protocol"+_name+".xml"); else saveProtocol(_filename); } public long getMaxPacketLength() { return (long)Math.pow(256, _sizeBytes); } public String toString() { return "Protocol "+this.getName()+" ("+this.getPort()+")"; } public String getPacketBufferType() { return _packetBufferType; } public boolean isStrictLength() { return _strictLength; } public boolean isAnalyzerEnabled() { return _analyzerEnabled; } @Deprecated public List<DataStructure> parseFile(String fileName) throws IOException { FileInputStream fis = new FileInputStream(new File(fileName)); List<DataStructure> list = new FastList<DataStructure>(); while(fis.available() > 0) { packetDirection dir; if(fis.read() == 0) { dir = packetDirection.clientPacket; } else { dir = packetDirection.serverPacket; } byte[] timeByte = new byte[8]; fis.read(timeByte,0,8); long time = new BigInteger(timeByte).longValue(); int lengthHi = 0; int lengthLo = 0; lengthLo = fis.read(); lengthHi = fis.read(); int length = lengthHi*256 + lengthLo; byte[] data = new byte [length]; fis.read(data,0,length); list.add(new DataPacket(data,dir,time, this)); } return list; } private void loadProtocol(String pFile) { Document doc = null; try { File file = new File(pFile); if(!file.exists()) { _clientPackets = new PacketFamilly("ClientPackets"); _clientPackets.setDirection(packetDirection.clientPacket); _serverPackets = new PacketFamilly("ServerPackets"); _serverPackets.setDirection(packetDirection.serverPacket); PacketSamurai.getUserInterface().log("Protocol file didnt exist ("+pFile+"), creating new protocol."); _filename = null; return; } FileInputStream fis = new FileInputStream(file); DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); factory.setValidating(false); factory.setIgnoringComments(false); DocumentBuilder docBuilder = factory.newDocumentBuilder(); docBuilder.setErrorHandler(new XMLErrorHandler()); try { doc = docBuilder.parse(fis); } catch (SAXParseException e) { Protocol.report("ERROR", e); return; } Node root = doc.getDocumentElement(); if(!root.getNodeName().equals("protocol")) { PacketSamurai.getUserInterface().log("Error malformed protocol : root node should be called 'protocol'."); return; } NamedNodeMap attr = root.getAttributes(); Node node = attr.getNamedItem("sizebytes"); if(node != null) { try { _sizeBytes = Integer.parseInt(node.getNodeValue()); } catch(NumberFormatException nfe) { PacketSamurai.getUserInterface().log("Warning: The sizebyte attribute of protocol must be an integer. Couldnt read the attribute provided, falling back to the default value"); _sizeBytes = 2 ; } } else { _sizeBytes = 2 ; } node = attr.getNamedItem("port"); if(node != null) { try { _port = Integer.parseInt(node.getNodeValue()); } catch(NumberFormatException nfe) { PacketSamurai.getUserInterface().log("Warning: The port attribute of protocol must be an integer. Couldnt read the attribute provided"); } } node = attr.getNamedItem("crypt"); if(node != null) { _encryption = node.getNodeValue(); } else { _encryption = "Null"; } node = attr.getNamedItem("strictlength"); if(node != null) { _strictLength = Boolean.parseBoolean(node.getNodeValue()); } node = attr.getNamedItem("analyzerEnabled"); if(node != null) { _analyzerEnabled = Boolean.parseBoolean(node.getNodeValue()); } node = attr.getNamedItem("packetbuffer"); if(node != null) { _packetBufferType = node.getNodeValue(); } node = attr.getNamedItem("checksumSize"); if(node != null) { _checksumSize = Integer.parseInt(node.getNodeValue()); } node = attr.getNamedItem("name"); if(node != null) { _name = node.getNodeValue(); } else { PacketSamurai.getUserInterface().log("Error while loading "+pFile+" : protocol has no 'name' attribute"); return; } for (Node n = root.getFirstChild(); n != null; n = n.getNextSibling()) { if ("packetfamilly".equalsIgnoreCase(n.getNodeName())) { PacketFamilly familly = parseFamilly(n, true); if(familly != null) { switch(familly.getDirection()) { case clientPacket: _clientPackets = familly; //set a default name if necessary if(_clientPackets.getName() == null) _clientPackets.setName("ClientPackets"); //Main.getInstance().log("Client Familly Ok"); break; case serverPacket: _serverPackets = familly; // set a default name if necessary if(_serverPackets.getName() == null) _serverPackets.setName("ServerPackets"); //Main.getInstance().log("Server Familly Ok"); break; default: PacketSamurai.getUserInterface().log("Error packetfamilly doesnt have a valid way"); } } else { PacketSamurai.getUserInterface().log("Error packetfamilly returned is null there was an error"); } } } _loaded = true; } catch (Exception e) { PacketSamurai.getUserInterface().log("Error while parsing protocol "+pFile); e.printStackTrace(); } } private PacketFamilly parseFamilly(Node n, boolean isRoot) { PacketFamilly newFamilly; NamedNodeMap attrs = n.getAttributes(); Node atr; if(isRoot) { newFamilly = new PacketFamilly(); } else { atr = attrs.getNamedItem("id"); if(atr == null) { PacketSamurai.getUserInterface().log("Error, packetfamilly is not root and has no id. skipping it:"); return null; } newFamilly = new PacketFamilly(Integer.decode(atr.getNodeValue())); } //------- atr = attrs.getNamedItem("way"); if(atr == null) { if(isRoot) { PacketSamurai.getUserInterface().log("Error, Root packetfamilly doesnt have 'way'. skipping it"); return null; } } else { String way = atr.getNodeValue(); if(way.equals("ServerPackets")) { newFamilly.setDirection(packetDirection.serverPacket); } else if(way.equals("ClientPackets")) { newFamilly.setDirection(packetDirection.clientPacket); } } //-------- atr = attrs.getNamedItem("switchtype"); if(atr == null) { PacketSamurai.getUserInterface().log("Error, packetfamilly doesnt have 'switchtype'. skipping it"); return null; } PartType idPart = PartTypeManager.getInstance().getType(atr.getNodeValue()); newFamilly.setSwitchType(idPart); newFamilly.addIdPartAtEnd(idPart); atr = attrs.getNamedItem("name"); if(atr != null) { newFamilly.setName(atr.getNodeValue()); } for (Node o = n.getFirstChild(); o != null; o = o.getNextSibling()) { if ("packet".equalsIgnoreCase(o.getNodeName())) { PacketFormat format = parseFormat(o); if(format != null) { format.addIdPartsAtBegining(newFamilly.getIdParts(),newFamilly.getIDs()); newFamilly.addNode(format); } } else if ("packetfamilly".equalsIgnoreCase(o.getNodeName())) { PacketFamilly familly = parseFamilly(o, false); if(familly != null) { familly.addIdPartsAtBegining(newFamilly.getIdParts(),newFamilly.getIDs()); newFamilly.addNode(familly); } } } return newFamilly; } private PacketFormat parseFormat(Node n) { PacketFormat newFormat; NamedNodeMap attrs = n.getAttributes(); Node atr; atr = attrs.getNamedItem("id"); if(atr == null) { PacketSamurai.getUserInterface().log("Error, packet doesnt have 'id'. skipping it"); return null; } int id = Integer.decode(atr.getNodeValue()); String name; atr = attrs.getNamedItem("name"); if(atr == null) { PacketSamurai.getUserInterface().log("Warning, packet doesnt have 'name'"); name = ""; } else { name = atr.getNodeValue(); } newFormat = new PacketFormat(id, name); newFormat.setContainingProtocol(this); boolean parseRet = false; try { parseRet = parseParts(n, newFormat.getDataFormat().getMainBlock()); } catch (IllegalStateException e) { PacketSamurai.getUserInterface().log("Error: parsing format: "+newFormat.toString()+" - Details: "+e.getMessage()); } if (!parseRet) { return null; } return newFormat; } private boolean parseParts(Node n, PartContainer pc) { NamedNodeMap attrs; Node atr; for (Node o = n.getFirstChild(); o != null; o = o.getNextSibling()) { if ("part".equalsIgnoreCase(o.getNodeName())) { Part pp = parsePart(o, pc); if(pp == null) return false; pc.addPart(pp); } else if ("for".equalsIgnoreCase(o.getNodeName())) { attrs = o.getAttributes(); int forId; atr = attrs.getNamedItem("id"); if(atr == null) { PacketSamurai.getUserInterface().log("Error, for doesnt have 'id'. skipping packet"); return false; } forId = Integer.parseInt(atr.getNodeValue()); atr = attrs.getNamedItem("analyzername"); String analyzerName = ""; if (atr != null) { analyzerName = atr.getNodeValue(); } ForPart newForPart = new ForPart(forId, analyzerName); newForPart.setParentContainer(pc); newForPart.setContainingFormat(pc.getContainingFormat()); if(parseParts(o, newForPart.getModelBlock())) { pc.addPart(newForPart); } else { return false; } } else if("switch".equalsIgnoreCase(o.getNodeName())) { attrs = o.getAttributes(); int switchId; atr = attrs.getNamedItem("id"); if(atr == null) { PacketSamurai.getUserInterface().log("Error, switch doesnt have 'id'. skipping packet"); return false; } switchId = Integer.parseInt(atr.getNodeValue()); atr = attrs.getNamedItem("analyzername"); String analyzerName = ""; if (atr != null) { analyzerName = atr.getNodeValue(); } SwitchPart newSwitchBlock = new SwitchPart(switchId, analyzerName); newSwitchBlock.setParentContainer(pc); newSwitchBlock.setContainingFormat(pc.getContainingFormat()); for(Node caseNode = o.getFirstChild(); caseNode != null; caseNode = caseNode.getNextSibling()) { if("case".equalsIgnoreCase(caseNode.getNodeName())) { attrs = caseNode.getAttributes(); String comment = null; atr = attrs.getNamedItem("comment"); if(atr != null) { comment = atr.getNodeValue(); } atr = attrs.getNamedItem("analyzername"); analyzerName = ""; if (atr != null) { analyzerName = atr.getNodeValue(); } atr = attrs.getNamedItem("id"); if(atr == null) { PacketSamurai.getUserInterface().log("Error, case doesnt have 'id'. skipping packet"); return false; } SwitchCaseBlock newSwitchCase = null; if(atr.getNodeValue().equalsIgnoreCase("default")) { newSwitchCase = new SwitchCaseBlock(newSwitchBlock, analyzerName); } else { int caseId; try { caseId = Integer.decode(atr.getNodeValue()); newSwitchCase = new SwitchCaseBlock(newSwitchBlock, caseId, analyzerName); } catch (NumberFormatException e) { PacketSamurai.getUserInterface().log("Warning, case doesnt have a valid 'id'. making it default"); newSwitchCase = new SwitchCaseBlock(newSwitchBlock, analyzerName); } } if(comment != null) newSwitchCase.setComment(comment); newSwitchCase.setParentContainer(pc); newSwitchCase.setContainingFormat(pc.getContainingFormat()); if(parseParts(caseNode, newSwitchCase)) { newSwitchBlock.addCase(newSwitchCase); } else { return false; } } } pc.addPart(newSwitchBlock); } } return true; } private Part parsePart(Node n, PartContainer pc) { String partName; NamedNodeMap attrs = n.getAttributes(); Node atr = attrs.getNamedItem("name"); if(atr == null) { PacketSamurai.getUserInterface().log("Warning, part doesnt have 'name'"); partName = ""; } else { partName = atr.getNodeValue(); } int partId; atr = attrs.getNamedItem("id"); if(atr == null) { partId = -1; } else { try { partId = Integer.parseInt(atr.getNodeValue()); } catch(NumberFormatException nfe) { PacketSamurai.getUserInterface().log("Warning: parts id must be an integer"); partId = -1; } } atr = attrs.getNamedItem("type"); if(atr == null) { PacketSamurai.getUserInterface().log("Error, part doesnt have 'type'. skipping packet"); return null; } String type = atr.getNodeValue(); int size = 0; int sizeid = -1; boolean dynBSize = false; atr = attrs.getNamedItem("size"); if(atr != null) { size = Integer.decode(atr.getNodeValue()); } else { atr = attrs.getNamedItem("sizeid"); if(atr != null) { sizeid = Integer.parseInt(atr.getNodeValue()); dynBSize = true; } } Reader r = null; String partComment = null; for (Node subNode = n.getFirstChild(); subNode != null; subNode = subNode.getNextSibling()) { if ("comment".equals(subNode.getNodeName())) { if(partComment != null) { PacketSamurai.getUserInterface().log("Warning, part '"+(partName)+"' has mutiple comments"); } partComment = subNode.getTextContent(); } if ("reader".equals(subNode.getNodeName())) { if(r != null) PacketSamurai.getUserInterface().log("Warning, part '"+(partName)+"' has mutiple readers"); NamedNodeMap attrs2 = subNode.getAttributes(); atr = attrs2.getNamedItem("type"); if(atr == null) { PacketSamurai.getUserInterface().log("Warning, part '"+(partName)+"' has a reader with no type"); continue; } //try default package Class<?> clazz = null; try { clazz = Class.forName("com.aionemu.packetsamurai.parser.valuereader."+atr.getNodeValue()+"Reader"); } catch (DOMException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { } if (clazz == null) { PacketSamurai.getUserInterface().log("Warning, part '"+(partName)+"' reader's could not be found in either parser or custom packages"); continue; } try { r = (Reader) clazz.newInstance(); if(!r.loadReaderFromXML(subNode)) //drop reader if loading went wrong r = null; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } } } if(partComment == null) partComment = ""; String partLookUpType; atr = attrs.getNamedItem("lookuptype"); if(atr == null) { partLookUpType = ""; } else { partLookUpType = atr.getNodeValue(); } String partAnalyzerName; atr = attrs.getNamedItem("analyzername"); if (atr == null) { partAnalyzerName = ""; } else { partAnalyzerName = atr.getNodeValue(); } Part pp = new Part(PartTypeManager.getInstance().getType(type), partId, partName, partComment, partLookUpType, partAnalyzerName); if (dynBSize) { pp.setBSizeId(sizeid); pp.setDynamicBSize(true); } else { pp.setBSize(size); pp.setDynamicBSize(false); } pp.setReader(r); return pp; } private void processParts(List<Part> parts, Element newNode, Document document) { for(Part part : parts) { Element partNode; if(part instanceof ForPart) { ForPart block = (ForPart)part; partNode = document.createElement("for"); partNode.setAttribute("id",String.valueOf(block.getForId())); processParts(block.getModelBlock().getParts(), partNode, document); } else if(part instanceof SwitchPart) { SwitchPart block = (SwitchPart) part; partNode = document.createElement("switch"); partNode.setAttribute("id",String.valueOf(block.getSwitchId())); for(SwitchCaseBlock sCase : block.getCases(true)) { Element caseNode = document.createElement("case"); if(sCase.isDefault()) caseNode.setAttribute("id", "default"); else caseNode.setAttribute("id", Integer.toString(sCase.getSwitchCase())); processParts(sCase.getParts(), caseNode, document); partNode.appendChild(caseNode); } } else { partNode = document.createElement("part"); partNode.setAttribute("type",part.getType().getName()); if(part.getId() != -1) partNode.setAttribute("id",String.valueOf(part.getId())); if(part.getName() != null && part.getName().length() > 0) partNode.setAttribute("name",part.getName()); if(part.getComment() != null && part.getComment().length() > 0) partNode.setAttribute("comment",part.getComment()); if(part.getAnalyzerName() != null && part.getAnalyzerName().length() > 0) partNode.setAttribute("analyzername", part.getLookUpType()); if(part.getType() instanceof RawPartType) { if(part.isDynamicBSize()) partNode.setAttribute("sizeid", String.valueOf(part.getBSizeId())); else if(part.getBSize() != 0) partNode.setAttribute("size", String.valueOf(part.getBSize())); } Reader reader = part.getReader(); if(reader !=null) { Element readerNode = document.createElement("reader"); readerNode.setAttribute("type", Util.getReaderName(reader.getClass()) ); reader.saveReaderToXML(readerNode, document); partNode.appendChild(readerNode); } } newNode.appendChild(partNode); } } private void buildDOM(ProtocolNode node, Element packetFamilly, Document document) { if(node instanceof PacketFormat) { PacketFormat format = (PacketFormat)node; Element newNode = document.createElement("packet"); newNode.setAttribute("id","0x"+Integer.toHexString(format.getID())); newNode.setAttribute("name",format.getName()); processParts(format.getDataFormat().getMainBlock().getParts(), newNode, document); packetFamilly.appendChild(newNode); } else if(node instanceof PacketFamilly) { PacketFamilly familly = (PacketFamilly)node; Element newNode = document.createElement("packetfamilly"); newNode.setAttribute("switchtype",familly.getSwitchType().getName()); newNode.setAttribute("id","0x"+Integer.toHexString(familly.getID())); // sort elements in family FastList<ProtocolNode> nodes = new FastList<ProtocolNode>(familly.getNodes().values()); Collections.sort(nodes, PROTOCOL_NODE_COMPARATOR); for(ProtocolNode pnode : nodes) { buildDOM(pnode, newNode, document); } packetFamilly.appendChild(newNode); } } public static void report(String severity, SAXParseException e) { PacketSamurai.getUserInterface().log(severity+": "+e.getMessage()+" (Line "+e.getLineNumber()+", Column: "+e.getColumnNumber()+")"); } class XMLErrorHandler implements ErrorHandler { public void error(SAXParseException e) throws SAXException { Protocol.report("Error", e); } public void fatalError(SAXParseException e) throws SAXException { Protocol.report("Fatal", e); } public void warning(SAXParseException e) throws SAXException { Protocol.report("Warning", e); } } }