package net.jtank.io; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; import java.io.*; import java.net.*; import net.jtank.util.Delegate; /** * WXML is a way of storing and transferring data in a hierarchical text format * It can't yet be technically called XML, as it does not nessesarily accept all * well-formed XML documents, it does handle most. * * * The WXML class contains some nessesary methods that relate to creating, * reading and writing the WXML file format. WXML is not XML, but is * syntactically very similar. * * * @author William Denniss * @version 1.1 - 3 April 2002 * * Version History: * 1.0 - 15 March 2002: Started. * 1.0 - 17 March 2002: Fully Functional. * 1.1 - 3 April 2002: TSFS integration added. * * * The tanksoftware package, it's binaries and source code are all licensed * under the GNU GPL. See the accompanying file COPYING for details. * * William Denniss has asserted his right under the Australian Copyright, * Designs and Patents Act 1988 to be identified as the author of this work. */ public class WXML extends WXMLSection { /** * Creates an empty WXML object, with no name, and no parent WXMLSection. */ public WXML () { super(null,null); } public static final int numTests = 4; /** * An example case. The file is read in, and then outputted to the screen. * Ideally, this output should semantically match the input. * @param args The arguments parsed into this method. */ public static final void main (String [] args) { int numTests = WXML.numTests; try { numTests = Integer.parseInt(args[0]); } catch (Exception e) {} for (int i = 1; i < numTests+1; i++) { try { System.out.println("loading test " + i); WXML newX = WXML.loadFile("data/WXML/test" + i + ".wxml"); System.out.println("test " + i + " loaded"); System.out.println("---"); System.out.println(newX.toWXMLString()); System.out.println("---"); System.out.println(newX.toOrderedWXMLString()); System.out.println("|||"); } catch (IOException e) { System.out.println("IO error reading file"); } catch (Exception e) { System.out.println("General Exception ---------"); e.printStackTrace(); System.out.println("|-------------|"); } } } /* public static String toEscape (String unEscaped) { String toReturn = ""; for (int i = 0; i < unEscaped.length(); i++) { if (unEscaped.charAt(i) == '=' || // equals unEscaped.charAt(i) == '\\' || // backslash unEscaped.charAt(i) == '<' || // less than unEscaped.charAt(i) == '>') { //greater than toReturn = toReturn + "\\" + unEscaped.charAt(i); } else { toReturn = toReturn + unEscaped.charAt(i); } } return toReturn; } public static String fromEscape (String escaped) { String toReturn = ""; int slash = 0; for (int i = 0; i < escaped.length(); i++) { if (escaped.charAt(i) != '\\' || slash > 0) { toReturn = toReturn + escaped.charAt(i); if (slash > 0) slash = 0; } else { slash++; } } return toReturn; } */ /** * Loads a WXML formatted file into a new WXML object. * * @param filename The String which points to the name of the file to load. * @param fileSystem The TSFS file system to use, can be null if none are being * used. * @return The WXML Object containing the file data, or null if there was an error. * @throws IOException If there is an error reading the file. */ public static WXML loadFile (String filename) throws IOException { String contents = ""; /* * Reads the entire file that is given into a string with no line breaks. */ BufferedReader br = null; FileUtil.getFileSpecificBufferedReader(filename); String line = br.readLine(); //todo: try putting line + "\n" back in. while (line != null) { contents += line + " \n";//" "; line = br.readLine(); } return parseString (contents); } /** * Parses a WXML formatted String into a WXML object. It does this by recursivly * adding the current WXML tag, and parsing the remaining text. A tag which * contains only text and no other tags is considered an attribute. * * @param contents The WXML formatted String to parse. * @return That String, as loaded into a WXML object. */ public static WXML parseString (String contents) { /* * Starts off the recursion method which will build the WXML tree. */ WXML toAdd = new WXML (); contents = replaceDoubleChar(contents,'\\','<','�'); contents = replaceDoubleChar(contents,'\\','>','�'); toAdd.addSubSection(parseSection(contents,null)); return toAdd; } //given a list of tags, eg asdf9040 // it will recursivly create sections and subsections for the first tag (level), then for all of the rest (balls) /** * Recursivly builds a List containing all of the WXMLSections as * they appear in the parsed text. This text would be in WXML format, * eg. <bold>foo</bold>bar. * That String would give 2 WXMLSections in the returned List file, the * first with a sub-section. * * @param toParse The String containing the WXML tags. * @param parent The parent object to which these tags belong. * @return A list containing the WXMLSections from the parsed String. */ public static List parseSection (String toParse, WXMLSection parent) { WXMLSection currentSection; List toReturn = new ArrayList(); String currentTag = ""; String currentTagInlineAttributes = ""; String currentSectionText = ""; int i = 0; // Skip to the first tag try { while (toParse.charAt(i) != '<') i++; // There are no further tags, only text ie, it is an attribute } catch (StringIndexOutOfBoundsException e) { WXMLSection toAdd = new WXMLSection ("-=attribute=-",parent); toAdd.text = toParse; //toAdd.addInOrderSectionTag toReturn.add(toAdd); return toReturn; } // Gets the text of the first tag i++; while (toParse.charAt(i) != ' ' && toParse.charAt(i) != '>') { currentTag += toParse.charAt(i); i++; } //System.out.println("++" + currentTag); // gets the inline attributes of the first tag if (toParse.charAt(i) == ' ') { while (toParse.charAt(i) != '>') { currentTagInlineAttributes += toParse.charAt(i); i++; } } i++; //System.out.println("++" + currentTagInlineAttributes); // gets all of the text contined in the tag try { while (!toParse.substring(i,currentTag.length() + 3 + i).equalsIgnoreCase("")) { //System.out.println("=" + toParse.substring(i,currentTag.length() + 3 + i) + "=." + "" + ".="); currentSectionText += toParse.charAt(i); i++; } //System.out.println("oooo3563456365365ooooooo"); } catch (Exception e) {/*System.out.println("ooooooooooo");*/}; String remainder = ""; // gets all of the text after the tag try { remainder = toParse.substring(currentTag.length() + 4 + i, toParse.length()); } catch (Exception e) {/*System.out.println("pppp");*/}; //System.out.println("+++" + currentSectionText); //System.out.println("++-+" + remainder); // Create new Section WXMLSection toAdd = new WXMLSection(currentTag, parent); toAdd.text = currentSectionText; // Add the text toAdd.addAttribute(inlineAttributeParser(currentTagInlineAttributes)); // Add attributes toAdd.addSubSection(parseSection (currentSectionText, parent)); // Add sub-sections // Add this subsection to the list to be returned toReturn.add( toAdd); // Add any other sections that were after this one to the list to be returned toReturn.addAll(parseSection(remainder, parent)); /* * NOTE that Sections containing only text and no sub-sections are considered * attributes, and at some stange need to be converted as such; * * todo: attribute resolution for inline and tagged attributes */ for (int l = 0; l < toReturn.size(); l++ ) ((WXMLSection) toReturn.get(l)).resolveSubSectionAttributes(); return toReturn; } public static String replaceChar (String toReplace, char from, char to) { String toReturn = ""; for (int i = 0; i < toReplace.length(); i++) { if (toReplace.charAt(i) == from) { toReturn += to; } else { toReturn += toReplace.charAt(i); } } return toReturn; } public static String replaceCharDouble (String toReplace, char from, char to1, char to2) { String toReturn = ""; for (int i = 0; i < toReplace.length(); i++) { if (toReplace.charAt(i) == from) { toReturn += to1 + "" + to2; } else { toReturn += toReplace.charAt(i); } } return toReturn; } public static String replaceDoubleChar (String toReplace, char from1, char from2, char to) { String toReturn = ""; for (int i = 0; i < toReplace.length()-1; i++) { if (toReplace.charAt(i) == from1 && toReplace.charAt(i+1) == from2) { toReturn += to; i++; } else { toReturn += toReplace.charAt(i); if (i == toReplace.length()-2) toReturn += toReplace.charAt(i+1); } } return toReturn; } /** * Parses a list of attributes that are embedded in a tag, eg * * @param toParse The String to parse for attributes * @return A List of WXMLAttribute objects which are all of the attributes * that were found in the tag */ public static List inlineAttributeParser (String toParse) { List toReturn = new ArrayList (); //System.out.println("1:" + toParse + "<"); toParse = replaceDoubleChar(toParse, '\\','\"','�'); //System.out.println("2:" +toParse + "<"); String spacesInAttribute = ""; // Temporarily remove spaces int j = 0; while (j < toParse.length()) { if (toParse.charAt(j) == '"') { spacesInAttribute += toParse.charAt(j); j++; //� while (toParse.charAt(j) != '"') { if (toParse.charAt(j) == ' ') spacesInAttribute += '�'; else if (toParse.charAt(j) == '=') spacesInAttribute += '�'; else spacesInAttribute += toParse.charAt(j); j++; } spacesInAttribute += toParse.charAt(j); j++; } else { spacesInAttribute += toParse.charAt(j); j++; } } String [] attributes = Delegate.stringCarver(spacesInAttribute, " "); //System.out.println(spacesInAttribute + "========================"); //System.out.println(attributes.length); for (int i = 0; i < attributes.length; i++) { String currentAttribute = ""; //insert spaces back in for (int k = 0; k < attributes[i].length(); k++) { if (attributes[i].charAt(k) == '�') currentAttribute += ' '; //re-insert spaces else if (attributes[i].charAt(k) == '"') currentAttribute += ""; //cut out "'s else currentAttribute += attributes[i].charAt(k); } String key = Delegate.carvePos(currentAttribute,0,"="); String value = Delegate.carvePos(currentAttribute,1,"="); value = replaceChar(value, '�','\"'); value = replaceChar(value, '�','='); value = replaceChar(value, '�','<'); value = replaceChar(value, '�','>'); //value = fromEscape(value); toReturn.add(new WXMLAttribute (key, value, true)); //System.out.println("key:" + key + "value:" + value); } return toReturn; } }