1// © 2016 and later: Unicode, Inc. and others. 2// License & terms of use: http://www.unicode.org/copyright.html#License 3/* 4 ****************************************************************************** 5 * Copyright (C) 2007-2010, International Business Machines Corporation and * 6 * others. All Rights Reserved. * 7 ****************************************************************************** 8 */ 9 10package com.ibm.icu.impl.duration.impl; 11 12import java.io.IOException; 13import java.io.Writer; 14import java.util.ArrayList; 15import java.util.List; 16 17import com.ibm.icu.lang.UCharacter; 18 19public class XMLRecordWriter implements RecordWriter { 20 private Writer w; 21 private List<String> nameStack; 22 23 public XMLRecordWriter(Writer w) { 24 this.w = w; 25 this.nameStack = new ArrayList<String>(); 26 } 27 28 @Override 29 public boolean open(String title) { 30 newline(); 31 writeString("<" + title + ">"); 32 nameStack.add(title); 33 return true; 34 } 35 36 @Override 37 public boolean close() { 38 int ix = nameStack.size() - 1; 39 if (ix >= 0) { 40 String name = nameStack.remove(ix); 41 newline(); 42 writeString("</" + name + ">"); 43 return true; 44 } 45 return false; 46 } 47 48 public void flush() { 49 try { 50 w.flush(); 51 } catch (IOException e) { 52 } 53 } 54 55 @Override 56 public void bool(String name, boolean value) { 57 internalString(name, String.valueOf(value)); 58 } 59 60 @Override 61 public void boolArray(String name, boolean[] values) { 62 if (values != null) { 63 String[] stringValues = new String[values.length]; 64 for (int i = 0; i < values.length; ++i) { 65 stringValues[i] = String.valueOf(values[i]); 66 } 67 stringArray(name, stringValues); 68 } 69 } 70 71 private static String ctos(char value) { 72 if (value == '<') { 73 return "<"; 74 } 75 if (value == '&') { 76 return "&"; 77 } 78 return String.valueOf(value); 79 } 80 81 @Override 82 public void character(String name, char value) { 83 if (value != '\uffff') { 84 internalString(name, ctos(value)); 85 } 86 } 87 88 @Override 89 public void characterArray(String name, char[] values) { 90 if (values != null) { 91 String[] stringValues = new String[values.length]; 92 for (int i = 0; i < values.length; ++i) { 93 char value = values[i]; 94 if (value == '\uffff') { 95 stringValues[i] = NULL_NAME; 96 } else { 97 stringValues[i] = ctos(value); 98 } 99 } 100 internalStringArray(name, stringValues); 101 } 102 } 103 104 @Override 105 public void namedIndex(String name, String[] names, int value) { 106 if (value >= 0) { 107 internalString(name, names[value]); 108 } 109 } 110 111 @Override 112 public void namedIndexArray(String name, String[] names, byte[] values) { 113 if (values != null) { 114 String[] stringValues = new String[values.length]; 115 for (int i = 0; i < values.length; ++i) { 116 int value = values[i]; 117 if (value < 0) { 118 stringValues[i] = NULL_NAME; 119 } else { 120 stringValues[i] = names[value]; 121 } 122 } 123 internalStringArray(name, stringValues); 124 } 125 } 126 127 public static String normalize(String str) { 128 if (str == null) { 129 return null; 130 } 131 StringBuilder sb = null; 132 boolean inWhitespace = false; 133 char c = '\0'; 134 boolean special = false; 135 for (int i = 0; i < str.length(); ++i) { 136 c = str.charAt(i); 137 if (UCharacter.isWhitespace(c)) { 138 if (sb == null && (inWhitespace || c != ' ')) { 139 sb = new StringBuilder(str.substring(0, i)); 140 } 141 if (inWhitespace) { 142 continue; 143 } 144 inWhitespace = true; 145 special = false; 146 c = ' '; 147 } else { 148 inWhitespace = false; 149 special = c == '<' || c == '&'; 150 if (special && sb == null) { 151 sb = new StringBuilder(str.substring(0, i)); 152 } 153 } 154 if (sb != null) { 155 if (special) { 156 sb.append(c == '<' ? "<" : "&"); 157 } else { 158 sb.append(c); 159 } 160 } 161 } 162 if (sb != null) { 163 /* 164 * if (c == ' ') { int len = sb.length(); if (len == 0) { return 165 * " "; } if (len > 1 && c == ' ') { sb.deleteCharAt(len - 1); } } 166 */ 167 return sb.toString(); 168 } 169 return str; 170 } 171 172 private void internalString(String name, String normalizedValue) { 173 if (normalizedValue != null) { 174 newline(); 175 writeString("<" + name + ">" + normalizedValue + "</" + name + ">"); 176 } 177 } 178 179 private void internalStringArray(String name, String[] normalizedValues) { 180 if (normalizedValues != null) { 181 push(name + "List"); 182 for (int i = 0; i < normalizedValues.length; ++i) { 183 String value = normalizedValues[i]; 184 if (value == null) { 185 value = NULL_NAME; 186 } 187 string(name, value); 188 } 189 pop(); 190 } 191 } 192 193 @Override 194 public void string(String name, String value) { 195 internalString(name, normalize(value)); 196 } 197 198 @Override 199 public void stringArray(String name, String[] values) { 200 if (values != null) { 201 push(name + "List"); 202 for (int i = 0; i < values.length; ++i) { 203 String value = normalize(values[i]); 204 if (value == null) { 205 value = NULL_NAME; 206 } 207 internalString(name, value); 208 } 209 pop(); 210 } 211 } 212 213 @Override 214 public void stringTable(String name, String[][] values) { 215 if (values != null) { 216 push(name + "Table"); 217 for (int i = 0; i < values.length; ++i) { 218 String[] rowValues = values[i]; 219 if (rowValues == null) { 220 internalString(name + "List", NULL_NAME); 221 } else { 222 stringArray(name, rowValues); 223 } 224 } 225 pop(); 226 } 227 } 228 229 private void push(String name) { 230 newline(); 231 writeString("<" + name + ">"); 232 nameStack.add(name); 233 } 234 235 private void pop() { 236 int ix = nameStack.size() - 1; 237 String name = nameStack.remove(ix); 238 newline(); 239 writeString("</" + name + ">"); 240 } 241 242 private void newline() { 243 writeString("\n"); 244 for (int i = 0; i < nameStack.size(); ++i) { 245 writeString(INDENT); 246 } 247 } 248 249 private void writeString(String str) { 250 if (w != null) { 251 try { 252 w.write(str); 253 } catch (IOException e) { 254 // if there's a problem, record it and stop writing 255 System.err.println(e.getMessage()); 256 w = null; 257 } 258 } 259 } 260 261 static final String NULL_NAME = "Null"; 262 private static final String INDENT = " "; 263} 264