DeveelDB  20151217
complete SQL database system, primarly developed for .NET/Mono frameworks
Properties.cs
Go to the documentation of this file.
1 //
2 // Copyright 2010-2015 Deveel
3 //
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
7 //
8 // http://www.apache.org/licenses/LICENSE-2.0
9 //
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
15 //
16 
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.IO;
21 using System.Text;
22 
23 namespace Deveel.Data.Util {
24  class Properties : Dictionary<string, string> {
25 
26  protected Properties defaults;
27 
31  public Properties() {
32  }
33 
39  public Properties(Properties defaults) {
40  this.defaults = defaults;
41  }
42 
56  public Object SetProperty(String key, String value) {
57  return this[key] = value;
58  }
59 
97  public void Load(Stream inStream) {
101  // The spec says that the file must be encoded using ISO-8859-1.
102  StreamReader reader = new StreamReader(inStream, Encoding.GetEncoding("ISO-8859-1"));
103  String line;
104 
105  while ((line = reader.ReadLine()) != null) {
106  char c = '\0';
107  int pos = 0;
108  // Leading whitespaces must be deleted first.
109  while (pos < line.Length
110  && Char.IsWhiteSpace(c = line[pos]))
111  pos++;
112 
113  // If empty line or begins with a comment character, skip this line.
114  if ((line.Length - pos) == 0
115  || line[pos] == '#' || line[pos] == '!')
116  continue;
117 
118  // The characters up to the next Whitespace, ':', or '='
119  // describe the key. But look for escape sequences.
120  // Try to short-circuit when there is no escape char.
121  int start = pos;
122  bool needsEscape = line.IndexOf('\\', pos) != -1;
123  StringBuilder key = needsEscape ? new StringBuilder() : null;
124  while (pos < line.Length
125  && !Char.IsWhiteSpace(c = line[pos++])
126  && c != '=' && c != ':') {
127  if (needsEscape && c == '\\') {
128  if (pos == line.Length) {
129  // The line continues on the next line. If there
130  // is no next line, just treat it as a key with an
131  // empty value.
132  line = reader.ReadLine();
133  if (line == null)
134  line = "";
135  pos = 0;
136  while (pos < line.Length
137  && Char.IsWhiteSpace(c = line[pos]))
138  pos++;
139  } else {
140  c = line[pos++];
141  switch (c) {
142  case 'n':
143  key.Append('\n');
144  break;
145  case 't':
146  key.Append('\t');
147  break;
148  case 'r':
149  key.Append('\r');
150  break;
151  case 'u':
152  if (pos + 4 <= line.Length) {
153  char uni = (char)Convert.ToInt32(line.Substring(pos, 4), 16);
154  key.Append(uni);
155  pos += 4;
156  } // else throw exception?
157  break;
158  default:
159  key.Append(c);
160  break;
161  }
162  }
163  } else if (needsEscape)
164  key.Append(c);
165  }
166 
167  bool isDelim = (c == ':' || c == '=');
168 
169  String keyString;
170  if (needsEscape)
171  keyString = key.ToString();
172  else if (isDelim || Char.IsWhiteSpace(c))
173  keyString = line.Substring(start, (pos - 1) - start);
174  else
175  keyString = line.Substring(start, pos - start);
176 
177  while (pos < line.Length
178  && Char.IsWhiteSpace(c = line[pos]))
179  pos++;
180 
181  if (!isDelim && (c == ':' || c == '=')) {
182  pos++;
183  while (pos < line.Length
184  && Char.IsWhiteSpace(c = line[pos]))
185  pos++;
186  }
187 
188  // Short-circuit if no escape chars found.
189  if (!needsEscape) {
190  this[keyString] = line.Substring(pos);
191  continue;
192  }
193 
194  // Escape char found so iterate through the rest of the line.
195  StringBuilder element = new StringBuilder(line.Length - pos);
196  while (pos < line.Length) {
197  c = line[pos++];
198  if (c == '\\') {
199  if (pos == line.Length) {
200  // The line continues on the next line.
201  line = reader.ReadLine();
202 
203  // We might have seen a backslash at the end of
204  // the file. The JDK ignores the backslash in
205  // this case, so we follow for compatibility.
206  if (line == null)
207  break;
208 
209  pos = 0;
210  while (pos < line.Length
211  && Char.IsWhiteSpace(c = line[pos]))
212  pos++;
213  element.EnsureCapacity(line.Length - pos + element.Length);
214  } else {
215  c = line[pos++];
216  switch (c) {
217  case 'n':
218  element.Append('\n');
219  break;
220  case 't':
221  element.Append('\t');
222  break;
223  case 'r':
224  element.Append('\r');
225  break;
226  case 'u':
227  if (pos + 4 <= line.Length) {
228  char uni = (char)Convert.ToInt32(line.Substring(pos, 4), 16);
229  element.Append(uni);
230  pos += 4;
231  } // else throw exception?
232  break;
233  default:
234  element.Append(c);
235  break;
236  }
237  }
238  } else
239  element.Append(c);
240  }
241  this[keyString] = element.ToString();
242  }
243  }
244 
254  [Obsolete("Use Store(Stream, string) method instead.")]
255  public void Save(Stream output, String header) {
256  try {
257  Store(output, header);
258  } catch (IOException) {
259  }
260  }
261 
296  public void Store(Stream output, String header) {
297  // The spec says that the file must be encoded using ISO-8859-1.
298  StreamWriter writer = new StreamWriter(output, Encoding.GetEncoding("ISO-8859-1"));
299  if (header != null)
300  writer.WriteLine("#" + header);
301  writer.WriteLine("#" + DateTime.Now);
302 
303  StringBuilder s = new StringBuilder(); // Reuse the same buffer.
304  foreach (var entry in this) {
305  FormatForOutput((String)entry.Key, s, true);
306  s.Append('=');
307  FormatForOutput((String)entry.Value, s, false);
308  writer.WriteLine(s);
309  }
310 
311  writer.Flush();
312  }
313 
328  public String GetProperty(String key) {
329  Properties prop = this;
330  // Eliminate tail recursion.
331  do {
332  String value = (String)prop[key];
333  if (value != null)
334  return value;
335  prop = prop.defaults;
336  }
337  while (prop != null);
338  return null;
339  }
340 
356  public String GetProperty(String key, String defaultValue) {
357  String prop = GetProperty(key);
358  if (prop == null)
359  prop = defaultValue;
360  return prop;
361  }
362 
363  public ICollection PropertyNames {
364  get {
365  // We make a new Set that holds all the keys, then return an enumeration
366  // for that. This prevents modifications from ruining the enumeration,
367  // as well as ignoring duplicates.
368  Properties prop = this;
369  var s = new List<string>();
370  // Eliminate tail recursion.
371  do {
372  s.AddRange(prop.Keys);
373  prop = prop.defaults;
374  } while (prop != null);
375  return s;
376  }
377  }
378 
390  public void List(Stream output) {
391  StreamWriter writer = new StreamWriter(output);
392  List(writer);
393  }
394 
395  public void List(StreamWriter output) {
396  output.WriteLine("-- listing properties --");
397 
398  foreach (var entry in this) {
399  output.Write((String)entry.Key + "=");
400 
401  String s = (String)entry.Value;
402  if (s != null && s.Length > 40)
403  output.WriteLine(s.Substring(0, 37) + "...");
404  else
405  output.WriteLine(s);
406  }
407  output.Flush();
408  }
409 
420  private static void FormatForOutput(String str, StringBuilder buffer, bool key) {
421  if (key) {
422  buffer.Length = 0;
423  buffer.EnsureCapacity(str.Length);
424  } else
425  buffer.EnsureCapacity(buffer.Length + str.Length);
426  bool head = true;
427  int size = str.Length;
428  for (int i = 0; i < size; i++) {
429  char c = str[i];
430  switch (c) {
431  case '\n':
432  buffer.Append("\\n");
433  break;
434  case '\r':
435  buffer.Append("\\r");
436  break;
437  case '\t':
438  buffer.Append("\\t");
439  break;
440  case ' ':
441  buffer.Append(head ? "\\ " : " ");
442  break;
443  case '\\':
444  case '!':
445  case '#':
446  case '=':
447  case ':':
448  buffer.Append('\\').Append(c);
449  break;
450  default:
451  if (c < ' ' || c > '~') {
452  String hex = ((int)c).ToString("{0:x4}");
453  buffer.Append("\\u0000".Substring(0, 6 - hex.Length));
454  buffer.Append(hex);
455  } else
456  buffer.Append(c);
457  break;
458  }
459  if (c != ' ')
460  head = key;
461  }
462  }
463 
464  /*
465  TODO:
466  public void storeToXML(Stream os, String comment) {
467  storeToXML(os, comment, "UTF-8");
468  }
469 
470  public void storeToXML(Stream os, String comment, String encoding) {
471  if (os == null)
472  throw new ArgumentNullException("os");
473  if (encoding == null)
474  throw new ArgumentNullException("encoding");
475  try {
476  DOMImplementationRegistry registry =
477  DOMImplementationRegistry.newInstance();
478  DOMImplementation domImpl = registry.getDOMImplementation("LS 3.0");
479  DocumentType doctype =
480  domImpl.createDocumentType("properties", null,
481  "http://java.sun.com/dtd/properties.dtd");
482  Document doc = domImpl.createDocument(null, "properties", doctype);
483  Element root = doc.getDocumentElement();
484  if (comment != null) {
485  Element commentElement = doc.createElement("comment");
486  commentElement.appendChild(doc.createTextNode(comment));
487  root.appendChild(commentElement);
488  }
489  Iterator iterator = entrySet().iterator();
490  while (iterator.hasNext()) {
491  Map.Entry entry = (Map.Entry)iterator.next();
492  Element entryElement = doc.createElement("entry");
493  entryElement.setAttribute("key", (String)entry.getKey());
494  entryElement.appendChild(doc.createTextNode((String)
495  entry.getValue()));
496  root.appendChild(entryElement);
497  }
498  DOMImplementationLS loadAndSave = (DOMImplementationLS)domImpl;
499  LSSerializer serializer = loadAndSave.createLSSerializer();
500  LSOutput output = loadAndSave.createLSOutput();
501  output.setByteStream(os);
502  output.setEncoding(encoding);
503  serializer.write(doc, output);
504  } catch (ClassNotFoundException e) {
505  throw (IOException)
506  new IOException("The XML classes could not be found.").initCause(e);
507  } catch (InstantiationException e) {
508  throw (IOException)
509  new IOException("The XML classes could not be instantiated.")
510  .initCause(e);
511  } catch (IllegalAccessException e) {
512  throw (IOException)
513  new IOException("The XML classes could not be accessed.")
514  .initCause(e);
515  }
516  }
517 
518  public void loadFromXML(InputStream input) {
519  if (input == null)
520  throw new NullPointerException("Null input stream supplied.");
521  try {
522  XMLInputFactory factory = XMLInputFactory.newInstance();
523  // Don't resolve external entity references
524  factory.setProperty("javax.xml.stream.isSupportingExternalEntities",
525  Boolean.FALSE);
526  XMLStreamReader reader = factory.createXMLStreamReader(input);
527  String name, key = null;
528  StringBuffer buf = null;
529  while (reader.hasNext()) {
530  switch (reader.next()) {
531  case XMLStreamConstants.START_ELEMENT:
532  name = reader.getLocalName();
533  if (buf == null && "entry".equals(name)) {
534  key = reader.getAttributeValue(null, "key");
535  if (key == null) {
536  String msg = "missing 'key' attribute";
537  throw new InvalidPropertiesFormatException(msg);
538  }
539  buf = new StringBuffer();
540  } else if (!"properties".equals(name) && !"comment".equals(name)) {
541  String msg = "unexpected element name '" + name + "'";
542  throw new InvalidPropertiesFormatException(msg);
543  }
544  break;
545  case XMLStreamConstants.END_ELEMENT:
546  name = reader.getLocalName();
547  if (buf != null && "entry".equals(name)) {
548  put(key, buf.toString());
549  buf = null;
550  } else if (!"properties".equals(name) && !"comment".equals(name)) {
551  String msg = "unexpected element name '" + name + "'";
552  throw new InvalidPropertiesFormatException(msg);
553  }
554  break;
555  case XMLStreamConstants.CHARACTERS:
556  case XMLStreamConstants.SPACE:
557  case XMLStreamConstants.CDATA:
558  if (buf != null)
559  buf.append(reader.getText());
560  break;
561  }
562  }
563  reader.close();
564  } catch (XMLStreamException e) {
565  throw (InvalidPropertiesFormatException)
566  new InvalidPropertiesFormatException("Error in parsing XML.").
567  initCause(e);
568  }
569  }
570  */
571  } // class Properties
572 }
A long string in the system.
Object SetProperty(String key, String value)
Definition: Properties.cs:56
Properties()
Creates a new empty property list with no default values.
Definition: Properties.cs:31
void List(Stream output)
Definition: Properties.cs:390
static void FormatForOutput(String str, StringBuilder buffer, bool key)
Formats a key or value for output in a properties file.
Definition: Properties.cs:420
String GetProperty(String key, String defaultValue)
Definition: Properties.cs:356
void Store(Stream output, String header)
Definition: Properties.cs:296
Properties(Properties defaults)
Definition: Properties.cs:39
void List(StreamWriter output)
Definition: Properties.cs:395
String GetProperty(String key)
Definition: Properties.cs:328
void Save(Stream output, String header)
Definition: Properties.cs:255