/* * Copyright (C) 2000 - 2008 TagServlet Ltd * * This file is part of Open BlueDragon (OpenBD) CFML Server Engine. * * OpenBD is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * Free Software Foundation,version 3. * * OpenBD 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenBD. If not, see http://www.gnu.org/licenses/ * * Additional permission under GNU GPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or combining * it with any of the JARS listed in the README.txt (or a modified version of * (that library), containing parts covered by the terms of that JAR, the * licensors of this Program grant you additional permission to convey the * resulting work. * README.txt @ http://www.openbluedragon.org/license/README.txt * * http://www.openbluedragon.org/ */ package com.naryx.tagfusion.xmlConfig; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Enumeration; import java.util.Hashtable; import java.util.List; import java.util.Vector; import com.nary.security.encrypter; import com.nary.util.Localization; import com.naryx.tagfusion.cfm.engine.cfArrayData; import com.naryx.tagfusion.cfm.engine.cfData; import com.naryx.tagfusion.cfm.engine.cfEngine; import com.naryx.tagfusion.cfm.engine.cfNumberData; import com.naryx.tagfusion.cfm.engine.cfStringData; import com.naryx.tagfusion.cfm.engine.cfStructData; import com.naryx.tagfusion.cfm.engine.cfmRunTimeException; public class xmlCFML { private static final String ENCRYPT_KEY = "c"; private Hashtable<String, Object> data; public xmlCFML() { data = new Hashtable<String, Object>(); } public xmlCFML(cfStructData admin) throws cfmRunTimeException { data = new Hashtable<String, Object>(); Object[] keys = admin.keys(); for (int i = 0; i < keys.length; i++) { String key = (String) keys[i]; cfData DD = admin.getData(key); if (DD.getDataType() == cfData.CFSTRUCTDATA) { // Only add the structure if it isn't empty if (!((cfStructData) DD).isEmpty()) data.put(key, new xmlCFML((cfStructData) DD)); } else if (DD.getDataType() == cfData.CFSTRINGDATA) { data.put(key, DD.getString()); } else if (DD.getDataType() == cfData.CFARRAYDATA) { cfArrayData AD = (cfArrayData) DD; for (int x = 0; x < AD.size(); x++) { DD = (cfStructData) AD.getData(new cfNumberData(x + 1)); data.put(key + "[" + DD.getData("name").getString() + "]", new xmlCFML((cfStructData) DD)); } } } } public void setData(String Key, String value) { int c1 = getDotIndex(Key); xmlCFML container; String subKey; if (c1 == -1) { container = this; subKey = Key; } else { container = getData(Key.substring(0, c1), false); subKey = Key.substring(c1 + 1); } // If this is a datasource password then we need to decrypt it if (Key.startsWith("server.cfquery.datasource") && subKey.equals("password")) container.set(subKey, encrypter.decryptDBpassword(value, ENCRYPT_KEY)); else container.set(subKey, value); } public void removeData(String Key) { int c1 = getDotIndex(Key); xmlCFML container; String subKey; if (c1 == -1) { container = this; subKey = Key; } else { container = getData(Key.substring(0, c1), true); // If the key isn't present then just return if (container == null) return; subKey = Key.substring(c1 + 1); } container.remove(subKey); } /* * getKeys * * This method returns a vector of keys or null. */ public Vector<String> getKeys(String Key) { boolean exists = true; Vector<String> keys = new Vector<String>(); int c1 = getDotIndex(Key); xmlCFML container; String subKey; if (c1 == -1) { container = this; subKey = Key; } else { container = getData(Key.substring(0, c1), true); if (container == null) { exists = false; } subKey = Key.substring(c1 + 1); } // If we found the key then add its entries to the keys vector if (exists) { String prefix = Key.substring(0, c1) + "."; subKey = subKey.substring(0, subKey.indexOf("[")); Enumeration<String> E = container.data.keys(); while (E.hasMoreElements()) { String t = E.nextElement(); // If this element is an array then remove the index portion String subt; int pos = t.indexOf('['); if (pos == -1) subt = t; else subt = t.substring(0, pos); // If there's a match then add it to the keys vector if (subt.equals(subKey)) keys.addElement(prefix + t); } } return (exists ? keys : null); } public String getString(String Key) { return getValue(Key); } public String getString(String Key, String _default) { String s = getString(Key); if (s != null) { return s; } return _default; } public long getLong(String Key, long _default) { String value = getValue(Key); if (value != null) { try { return Long.parseLong(value); } catch (NumberFormatException E) { } } return _default; } public int getInt(String Key, int _default) { String value = getValue(Key); if (value != null) { try { return Integer.parseInt(value); } catch (NumberFormatException E) { } } return _default; } public boolean getBoolean(String Key, boolean _default) { String value = getValue(Key); if (value != null) { return Boolean.valueOf(value).booleanValue(); } return _default; } private void set(String Key, xmlCFML value) { data.put(Key.toLowerCase().trim(), value); } private void set(String Key, String value) { data.put(Key.toLowerCase().trim(), value.trim()); } private void remove(String Key) { data.remove(Key.toLowerCase().trim()); } // ------------------------------------- /* * dump * * This method is for debugging. */ public void dump(int _tab) { try { Enumeration<String> keys = data.keys(); while (keys.hasMoreElements()) { Object next = keys.nextElement(); Object nextVal = data.get(next); if (nextVal instanceof xmlCFML) { cfEngine.log(padOut(_tab) + next); ((xmlCFML) nextVal).dump(_tab + 2); } else { cfEngine.log(padOut(_tab) + next + " : " + nextVal); } } } catch (Exception e) { e.printStackTrace(); } } private xmlCFML getData(String _key, boolean returnNull) { xmlCFML currentPtr = this, newOne; int dotIndex = getDotIndex(_key); if (dotIndex == -1) { newOne = (xmlCFML) data.get(_key); if (newOne == null) { if (returnNull) { return null; } else { newOne = new xmlCFML(); currentPtr.set(_key, newOne); } } return newOne; } CustomStringTokenizer ST = new CustomStringTokenizer(_key, '.'); while (ST.hasMoreTokens()) { String subkey = ST.nextToken(); newOne = currentPtr.getData(subkey, returnNull); if (newOne == null) { if (returnNull) { return null; } else { newOne = new xmlCFML(); currentPtr.set(subkey, newOne); } } currentPtr = newOne; } return currentPtr; } // ------------------------------------- /* * getValue * * This method tries to retrieve the key value, returning null if not found. */ private String getValue(String Key) { String value = null; int c1 = getDotIndex(Key); xmlCFML container; String subKey; if (c1 == -1) { container = this; subKey = Key; } else { container = getData(Key.substring(0, c1), true); subKey = Key.substring(c1 + 1); } // If we found the key then get the value if (container != null) value = (String) container.data.get(subKey); return value; } // -------------------------------- /** * getCFMLData * * Returns the data in the form of a cfStructData. */ public cfStructData getCFMLData() throws cfmRunTimeException { cfStructData topStruct = new cfStructData(); Enumeration<String> E = data.keys(); while (E.hasMoreElements()) { String key = E.nextElement(); Object D = data.get(key); // If the data is a string then just add it to the structure if (D instanceof String) { topStruct.setData(key, new cfStringData((String) D)); } else { // If the data isn't an array then add it to the structure as a // structure int c1 = key.indexOf("["); if (c1 == -1) { topStruct.setData(key, ((xmlCFML) D).getCFMLData()); } else { // It's an array element so add it to the array String subKey = key.substring(0, c1); String arrayName = key.substring(c1 + 1, key.length() - 1); cfArrayData arrayData = (cfArrayData) topStruct.getData(subKey); if (arrayData == null) { arrayData = cfArrayData.createArray(1); topStruct.setData(subKey, arrayData); } cfStructData tmp = ((xmlCFML) D).getCFMLData(); tmp.setData("name", new cfStringData(arrayName)); arrayData.addElement(tmp); } } } return topStruct; } // -------------------------------- public void printXML(String name, PrintWriter Out) { printXML(name, -2, Out); } public void printXML(String name, int tabSize, PrintWriter Out) { String keyN = name; if (name.length() > 0) { int c1 = name.indexOf("["); if (c1 == -1) { Out.println(padOut(tabSize) + "<" + name + ">"); keyN = name; } else { keyN = name.substring(0, c1); Out.println(padOut(tabSize) + "<" + keyN + " name=\"" + xmlEscape(name.substring(c1 + 1, name.length() - 1)) + "\">"); } } tabSize += 2; Enumeration<String> E = data.keys(); String key; while (E.hasMoreElements()) { key = E.nextElement(); Object D = data.get(key); if (D instanceof String) { // If this is a datasource password then we need to encrypt it if (keyN.equals("datasource") && key.equals("password")) Out.println(padOut(tabSize) + "<" + key + ">" + xmlEscape(encrypter.encryptDBpassword((String) D, ENCRYPT_KEY)) + "</" + key + ">"); else Out.println(padOut(tabSize) + "<" + key + ">" + xmlEscape((String) D) + "</" + key + ">"); } else { ((xmlCFML) D).printXML(key, tabSize, Out); } } tabSize -= 2; if (keyN.length() > 0) Out.println(padOut(tabSize) + "</" + keyN + ">"); } private static String padOut(int number) { String tmp = ""; for (int x = 0; x < number; x++) tmp += " "; return tmp; } private String xmlEscape(String _value) { return com.nary.util.string.escapeHtml(_value); } // this returns the last index of '.' ignoring '.'s that are within [ ] private static int getDotIndex(String _str) { int rBindex = _str.lastIndexOf(']'); int lBindex = _str.lastIndexOf('['); int dotIndex = _str.lastIndexOf('.'); if (dotIndex == -1) { return -1; } else if (dotIndex > rBindex) { return dotIndex; } else { dotIndex = _str.lastIndexOf('.', lBindex); return dotIndex; } } public void writeTo(File _dest) throws IOException { writeTo("", _dest); } public void writeTo(String name, File _dest) throws IOException { OutputStream fOut = cfEngine.thisPlatform.getFileIO().getFileOutputStream(_dest); OutputStreamWriter outW = new OutputStreamWriter(fOut, Localization.convertCharSetToCharEncoding("UTF-8")); PrintWriter outStream = new PrintWriter(outW); outStream.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); printXML(name, outStream); outStream.flush(); outStream.close(); } // -------------------------------- /** * This is a string tokenizer with the difference that the occurrence of the * delimiter between [ ]'s is ignored. */ private class CustomStringTokenizer { List<String> tokens; int current; int noTokens; CustomStringTokenizer(String _s, char _sep) { tokens = new ArrayList<String>(); current = 0; int start = 0; boolean escape = false; char nextCh; for (int i = 0; i < _s.length(); i++) { nextCh = _s.charAt(i); if (nextCh == _sep && !escape) { tokens.add(_s.substring(start, i)); start = i + 1; } else if (nextCh == '[' && !escape) { escape = true; } else if (nextCh == ']' && escape) { escape = false; } } if (start > 0) { tokens.add(_s.substring(start)); } noTokens = tokens.size(); } boolean hasMoreTokens() { return current < noTokens; } String nextToken() { return tokens.get(current++); } } }