Package acdp.misc

Class Layout

java.lang.Object
acdp.misc.Layout

public final class Layout extends Object
Defines a layout, that is, a collection of well-structured values. A layout can be easier read by a human being than an XML or JSON formatted text but it is not that powerful, though more powerful than just a flat set of properties, e.g., name = john, age = 38.

The syntax of a layout in EBNF (Extended Backus–Naur Form) is recursively defined as follows:

 layout      = [ entryList, [EOL] ]
 entryList   = indEntry, { EOL, indEntry }
 indEntry    = [ comment ], indent, entry
 indent      = level * INDENT
 entry       = KEY, WS, "=", WS, VALUE |
               KEY, "[]", seqVal |
               KEY, layoutVal
 layoutVal   = [ EOL, entryList ]
 seqVal      = { EOL, [ comment ], indent, element }
 element     = TEXT |
               "[]", seqVal |
               ".", EOL, layoutVal
 comment     = cLine, { cLine }
 cLine       = indent, "#", CTEXT, EOL
 
 EOL         = ? any predefined non-empty sequence of characters
                 identifying the end of a line. ?
 INDENT      = ? any predefined non-empty sequence of characters with
                 codes less than or equal to SPACE U+0020 and not
                 containing EOL. ?
 WS          = ? any empty or non-empty sequence of characters with
                 codes less than or equal to SPACE U+0020 and not
                 containing EOL. ?
 KEY         = ? any non-empty sequence of characters that starts
                 and ends with a character having a code greater than
                 SPACE U+0020 and that starts with a character different from
                 the NUMBER SIGN ('#') U+0023 and that neither
                 contains the EQUALS SIGN ('=') U+003D nor EOL. ?
 VALUE       = ? any empty or non-empty sequence of characters that
                 starts and ends with a character having a code greater
                 than SPACE U+0020 and that does not contain EOL. ?
 TEXT        = ? any non-empty sequence of characters that starts
                 and ends with a character having a code greater than
                 SPACE U+0020 and that starts with a character different from
                 the NUMBER SIGN ('#') U+0023 and that does not
                 contain EOL. ?
 CTEXT       = ? any empty or non-empty sequence of characters that does not
                 contain EOL. ?
The terminal symbol EOL denotes a line terminator and the symbol level denotes an integer variable called the level of indentation. Initially this variable is set to 0 and it is incremented by one each time a layoutVal or seqVal non-terminal symbol is started to be applied and it is decremented by one each time that layoutVal or that seqVal non-terminal symbol is finished to be applied.

Thus, a layout can roughly be seen as a collection of key-value pairs where the key is some text and the value is either a string, a sequence or again a layout. A key-value pair with a value being a string is also known as a property, e.g., "age = 38". An element of a sequence can be a string, again a sequence or a layout.

Applying three space characters for indentation, a database with a name equal to "TriasDB" containing a single table "Configuration" may be laid out like this:

 name = TriasDB
 version = 1.5
 consistencyNumber = 0
 # cipherFactoryClassName = com.example.CipherFactory
 # cipherChallenge = 29jxv5pqc56i69ys
 forceWriteCommit = off
 recFile = rec
 tables
    Configuration
       columns[]
          .
             name = ID
             typeDesc = s!i58v
          .
             name = Locales
             typeDesc = s!i40v
          .
             name = Timestamp
             typeDesc = l!i8
       store
          nobsRowRef = 1
          flDataFile = Configuration_fld
Due to its recursive definition the entries under tables, Configuration, store, and the dots (.) are themselves layouts. Note that empty layouts, empty sequences and even empty string property values are allowed.

Indentation is the only way to give a layout a more complex structure than just a flat set of key-value pairs. Changing the indentations manually within a file containing a layout must be done with care. It is an error if a line of text starts with a sequence of white spaces that can't be matched to an indentation. (A white space is a character with a code less than or equal to SPACE U+0020.) It is also an error if any line of text forming an entry in a layout or forming an element in a sequence has an indentation level that is too large, like the last line of text in the following example:

 store
    nobsRowRef = 1
       flDataFile = Configuration_fld

Note, however, that

 store
    nobsRowRef = 1
 flDataFile = Configuration_fld
is perfectly correct, though, this might not be what the user wanted because the last entry is an entry of the "root" layout and not an entry of the nested store layout.

Note that comments must be properly indented. Therefore

 #
 # This is a store
 #
 store
    nobsRowRef = 1
    # The backing file of the store
    flDataFile = Configuration_fld

is perfectly correct, whereas

 store
    nobsRowRef = 1
   # The backing file of the store
    flDataFile = Configuration_fld

and

 store
    nobsRowRef = 1
 # Now comes the table class name
    flDataFile = Configuration_fld
raise an exception because the comment is not properly indented.

This class provides the following operations:

  • Two factory methods for creating a layout by reading it from a UTF-8 encoded text file and input stream, respectively.
  • A constructor for creating an empty layout.
  • A copy constructor for creating a memory independent copy of an already existing layout.
  • Methods for creating, returning and manipulating the entries of a layout.
  • A method for saving the changes made to a layout.
  • Two methods for writing a layout to a UTF-8 encoded text file and output stream, respectively.
Author:
Beat Hörmann
  • Constructor Details

    • Layout

      public Layout()
      Constructs a new empty layout.
    • Layout

      public Layout(Layout layout) throws NullPointerException
      Copy constructor. Creates a new layout and deeply copies the elements of the specified layout into it. The created layout is memory independent of the specified layout.
      Parameters:
      layout - The layout to copy, not allowed to be null.
      Throws:
      NullPointerException - If layout is null.
  • Method Details

    • fromFile

      Constructs a layout by reading it from the specified file. By convention, the first line of text that does not contain white spaces only and that starts with a (series of) white space(s) defines the indentation. (A white space is a character with a code less than or equal to '\u0020', the code of SPACE U+0020.)

      Blank lines, hence, lines of text containing white spaces only or even no characters at all are tolerated as well as lines of text ending with white spaces immediately before the line terminator. Note, however, that such extra sugar, unlike comments, is not retained when the layout is written back to the same file or to another file or output stream calling the toFile(java.nio.file.Path, java.lang.String, java.nio.file.OpenOption...), save() or toOutputStream(java.io.OutputStream, java.lang.String) method.

      This method assumes the characters in the file to be coded in UTF-8.

      Parameters:
      file - The file containing the layout, not allowed to be null.
      Returns:
      The layout.
      Throws:
      NullPointerException - If file is null.
      IndentationException - If a line of text is not properly indented. (See the class description to learn about a line of text that is not properly indented.)
      DanglingCommentException - If there exists a comment that is not properly followed or not followed at all by an entry or an element of a sequence.
      IOException - If the specified file does not exist or if another I/O error occurs.
    • fromInputStream

      public static final Layout fromInputStream(InputStream inputStream) throws NullPointerException, IndentationException, DanglingCommentException, IOException
      Constructs a layout by reading it from the specified input stream. Apart from reading the layout from a stream rather than from a file invoking this factory method has exactly the same effect as invoking the fromFile(java.nio.file.Path) factory method.

      The specified input stream is not closed.

      Parameters:
      inputStream - The input stream from where to read the layout, not allowed to be null.
      Returns:
      The layout, never null but may be an empty layout.
      Throws:
      NullPointerException - If inputStream is null.
      IndentationException - If a line of text is not properly indented. (See the class description to learn about a line of text that is not properly indented.)
      DanglingCommentException - If there exists a comment that is not properly followed or not followed at all by an entry or an element of a sequence.
      IOException - If an I/O error occurs.
    • toMap

      public final Map<String,Object> toMap()
      Converts this layout to an unmodifiable map. The keys and the values of the returned map are the keys and the values of this layout. The values are of type String, Seq or Layout.
      Returns:
      The layout as an unmodifiable map.
    • entries

      public final Layout.LtEntry[] entries()
      Returns the entries of this layout. The entries are sorted in the order of their creation. For example, if this layout was obtained from a file or an input stream then the entries appear in the same order as they were originally read from the input stream or the file.
      Returns:
      The sorted array of entries of this layout, never null.
    • size

      public final int size()
      Returns the number of entries in this layout.
      Returns:
      The number of entries in this layout.
    • contains

      public final boolean contains(String key) throws NullPointerException
      Returns true if this layout contains an entry with the specified key.
      Parameters:
      key - The key whose presence in the layout is to be tested.
      Returns:
      The boolean value true if this layout contains an entry with the specified key.
      Throws:
      NullPointerException - If the specified key is null.
    • getString

      Returns the string with the specified key.
      Parameters:
      key - The key whose associated string is to be returned.
      Returns:
      The string with the specified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified key.
      NullPointerException - If the specified key is null.
      ClassCastException - If the stored value with the specified key is not a string.
    • getSeq

      Returns the sequence with the specified key.
      Parameters:
      key - The key whose associated sequence is to be returned.
      Returns:
      The sequence with the specified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified key.
      NullPointerException - If the specified key is null.
      ClassCastException - If the stored value with the specified key is not a sequence.
    • getLayout

      Returns the layout with the specified key.
      Parameters:
      key - The key whose associated layout is to be returned.
      Returns:
      The layout with the specified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified key.
      NullPointerException - If the specified key is null.
      ClassCastException - If the stored value with the specified key is not a layout.
    • getStringByQualKey

      public final String getStringByQualKey(String qualKey) throws MissingEntryException, NullPointerException, ClassCastException
      Returns the string with the specified qualified key.

      The result and behaviour of invoking this method is identical to the result and behaviour of invoking the getStringByQualKey(String, char) method with a separator character equal to the dot ('.') character.

      Parameters:
      qualKey - The qualified key whose associated string is to be returned.
      Returns:
      The string with the specified qualified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified qualified key.
      NullPointerException - If the specified qualified key is null.
      ClassCastException - If the stored value of one of the layout keys of the specified qualified key is not a layout or if the stored value with the last key is not a string.
    • getStringByQualKey

      public final String getStringByQualKey(String qualKey, char separator) throws MissingEntryException, NullPointerException, ClassCastException
      Returns the string with the specified qualified key, the keys separated by the specified separator character.

      A qualified key denotes a sequence of normal keys where the keys are separated from each other by the specified separator character, as, for instance, in profession.words.tableClassName with a separator character equal to the dot ('.'). All but the last key must map to a layout and are therefore called the layout keys. (In the example above, profession and words are layout keys.)

      A qualified key not containing the separator character has no layout keys and thus reduces to a normal key.

      Parameters:
      qualKey - The qualified key whose associated string is to be returned.
      separator - The separator character.
      Returns:
      The string with the specified qualified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified qualified key.
      NullPointerException - If the specified qualified key is null.
      ClassCastException - If the stored value of one of the layout keys of the specified qualified key is not a layout or if the stored value with the last key is not a string.
    • getSeqByQualKey

      public final Layout.Seq getSeqByQualKey(String qualKey) throws MissingEntryException, NullPointerException, ClassCastException
      Returns the sequence with the specified qualified key.

      The result and behaviour of invoking this method is identical to the result and behaviour of invoking the getSeqByQualKey(String, char) method with a separator character equal to the dot ('.') character.

      Parameters:
      qualKey - The qualified key whose associated sequence is to be returned.
      Returns:
      The sequence with the specified qualified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified qualified key.
      NullPointerException - If the specified qualified key is null.
      ClassCastException - If the stored value of one of the layout keys of the specified qualified key is not a layout or if the stored value with the last key is not a sequence.
    • getSeqByQualKey

      public final Layout.Seq getSeqByQualKey(String qualKey, char separator) throws MissingEntryException, NullPointerException, ClassCastException
      Returns the sequence with the specified qualified key, the keys separated by the specified separator character.

      A qualified key denotes a sequence of normal keys where the keys are separated from each other by the specified separator character, as, for instance, in profession.words.tableClassName with a separator character equal to the dot ('.'). All but the last key must map to a layout and are therefore called the layout keys. (In the example above, profession and words are layout keys.)

      A qualified key not containing the separator character has no layout keys and thus reduces to a normal key.

      Parameters:
      qualKey - The qualified key whose associated sequence is to be returned.
      separator - The separator character.
      Returns:
      The sequence with the specified qualified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified qualified key.
      NullPointerException - If the specified qualified key is null.
      ClassCastException - If the stored value of one of the layout keys of the specified qualified key is not a layout or if the stored value with the last key is not a sequence.
    • getLayoutByQualKey

      public final Layout getLayoutByQualKey(String qualKey) throws MissingEntryException, NullPointerException, ClassCastException
      Returns the layout with the specified qualified key.

      The result and behaviour of invoking this method is identical to the result and behaviour of invoking the getLayoutByQualKey(String, char) method with a separator character equal to the dot ('.') character.

      Parameters:
      qualKey - The qualified key whose associated layout is to be returned.
      Returns:
      The layout with the specified qualified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified qualified key.
      NullPointerException - If the specified qualified key is null.
      ClassCastException - If the stored value of one of the keys of the specified qualified key is not a layout.
    • getLayoutByQualKey

      public final Layout getLayoutByQualKey(String qualKey, char separator) throws MissingEntryException, NullPointerException, ClassCastException
      Returns the layout with the specified qualified key, the keys separated by the specified separator character.

      A qualified key denotes a sequence of normal keys where the keys are separated from each other by the specified separator character, as, for instance, in profession.words.tableClassName with a separator character equal to the dot ('.'). All but the last key must map to a layout and are therefore called the layout keys. (In the example above, profession and words are layout keys.)

      A qualified key not containing the separator character has no layout keys and thus reduces to a normal key.

      Parameters:
      qualKey - The qualified key whose associated layout is to be returned.
      separator - The separator character.
      Returns:
      The layout with the specified qualified key, never null.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified qualified key.
      NullPointerException - If the specified qualified key is null.
      ClassCastException - If the stored value of one of the keys of the specified qualified key is not a layout.
    • add

      public final Layout add(String key, String value) throws IllegalArgumentException, NullPointerException
      Adds the specified string value with the specified key.
      Parameters:
      key - The key with which the specified string value is to be associated.
      value - The string value to be associated with the specified key.
      Returns:
      This layout.
      Throws:
      IllegalArgumentException - If the layout already contains an entry with the specified key.
      NullPointerException - If the specified key or string value is null.
    • addSeq

      Creates a new sequence, adds it to this layout with the specified key and returns the new sequence.
      Parameters:
      key - The key with which the new sequence is to be associated.
      Returns:
      The newly created empty sequence, never null.
      Throws:
      IllegalArgumentException - If the layout already contains an entry with the specified key.
      NullPointerException - If the specified key is null.
    • add

      Adds the specified sequence with the specified key.
      Parameters:
      key - The key with which the specified sequence is to be associated.
      seq - The sequence to be associated with the specified key.
      Returns:
      This layout.
      Throws:
      IllegalArgumentException - If the layout already contains an entry with the specified key.
      NullPointerException - If the specified key or sequence is null.
    • addLayout

      public final Layout addLayout(String key) throws IllegalArgumentException, NullPointerException
      Creates a new layout, adds it to this layout with the specified key and returns the new layout.
      Parameters:
      key - The key with which the new layout is to be associated.
      Returns:
      The newly created empty layout, never null.
      Throws:
      IllegalArgumentException - If the layout already contains an entry with the specified key.
      NullPointerException - If the specified key is null.
    • add

      public final Layout add(String key, Layout layout) throws IllegalArgumentException, NullPointerException
      Adds the specified layout with the specified key.
      Parameters:
      key - The key with which the specified layout is to be associated.
      layout - The layout to be associated with the specified key.
      Returns:
      This layout.
      Throws:
      IllegalArgumentException - If the layout already contains an entry with the specified key.
      NullPointerException - If the specified key or layout is null.
    • replace

      Replaces the stored string value having the specified key with the specified string value.
      Parameters:
      key - The key whose value is to be replaced.
      value - The string value, not allowed to be null.
      Returns:
      This layout.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified key.
      NullPointerException - If the specified key or string value is null.
      ClassCastException - If the stored value with the specified key is not a string.
    • replace

      Replaces the stored sequence having the specified key with the specified sequence.
      Parameters:
      key - The key whose value is to be replaced.
      seq - The sequence, not allowed to be null.
      Returns:
      This layout.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified key.
      NullPointerException - If the specified key or sequence is null.
      ClassCastException - If the stored value with the specified key is not a sequence.
    • replace

      public final Layout replace(String key, Layout layout) throws MissingEntryException, NullPointerException, ClassCastException
      Replaces the stored layout having the specified key with the specified layout.
      Parameters:
      key - The key whose value is to be replaced.
      layout - The layout, not allowed to be null.
      Returns:
      This layout.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified key.
      NullPointerException - If the specified key or layout is null.
      ClassCastException - If the stored value with the specified key is not a layout.
    • replaceKey

      Replaces the specified current key with the specified new key.

      This method has no effect if the current key is equal to the new key.

      Parameters:
      key - The current key.
      newKey - The new key.
      Returns:
      This layout.
      Throws:
      MissingEntryException - If the layout contains no entry with the specified current key.
      IllegalArgumentException - If the layout already contains an entry with the specified new key.
      NullPointerException - If one of the arguments is null.
    • remove

      public final Layout remove(String key) throws MissingEntryException, NullPointerException
      Removes the entry with the specified key.
      Parameters:
      key - The key whose entry is to be removed.
      Returns:
      This layout.
      Throws:
      MissingEntryException - If the layout contains no entry for the specified key.
      NullPointerException - If the specified key is null.
    • indent

      public final String indent()
      Returns the indentation discovered in the file or input stream when this layout was constructed with the fromFile(java.nio.file.Path) or fromInputStream(java.io.InputStream) factory method, respectively.
      Returns:
      The indentation, never an empty string. The value is null if the layout was not constructed with one of the factory methods mentioned above or if the layout has no indented elements.
    • toFile

      public final void toFile(Path file, String indent, OpenOption... options) throws IllegalArgumentException, NullPointerException, UnsupportedOperationException, IOException
      Writes this layout to the specified file. Nested layouts and sequences are indented using the specified indentation or the tab character (CHARACTER TABULATION (U+0009)) if the specified indentation is null or an empty string. The written layout strictly conforms to the syntax rules given in the class description.

      The entries of a layout appear in the order of their creation. This guarantees that a layout that was originally read from a file or an input stream (see fromFile(java.nio.file.Path) or fromInputStream(java.io.InputStream)) looks "similar" to the layout later written back to the same file or to another file. Comments are retained. However, blank lines in the original file and the character (or sequence of characters) used for indendation, as well as extra white spaces immediately before the end of a line of text are not retained. Furthermore, the equal character ('=') in a property is surrounded by a single space character, ignoring any other surrounding sequences of white spaces in the original file or input stream.

      Uses UTF-8 for character encoding.

      This method fails if the file is locked.

      Parameters:
      file - The file to which to write this layout, not allowed to be null.
      indent - The indentation to use for nested layouts or null in which case an indentation equal to CHARACTER TABULATION U+0009 is used.
      options - The options specifying how the file is opened, see the Files.write method description for any details.
      Throws:
      IllegalArgumentException - If the specified indentation contains at least one character that is not a white space.
      NullPointerException - If the specified file is null.
      UnsupportedOperationException - If an unsupported option is specified.
      IOException - If an I/O error occurs.
    • save

      public final void save() throws UnsupportedOperationException, IOException
      Invokes the toFile(java.nio.file.Path, java.lang.String, java.nio.file.OpenOption...) method with the file identical to the file argument of the fromFile(java.nio.file.Path) factory method and with no open options.
      Throws:
      UnsupportedOperationException - If this layout was constructed with a constructor different from the fromFile factory method.
      IOException - If an I/O error occurs.
    • toOutputStream

      public final void toOutputStream(OutputStream stream, String indent) throws IllegalArgumentException, NullPointerException, IOException
      Writes this layout to the specified output stream. Apart from writing the layout to a stream rather than to a file invoking this method has exactly the same effect as invoking the toFile(java.nio.file.Path, java.lang.String, java.nio.file.OpenOption...) method.

      The specified output stream is not closed.

      Parameters:
      stream - The output stream to which to write this layout, not allowed to be null.
      indent - The indentation to use for nested layouts or null in which case an indentation equal to CHARACTER TABULATION U+0009 is used.
      Throws:
      IllegalArgumentException - If the specified indentation contains at least one character that is not a white space.
      NullPointerException - If the specified stream is null.
      IOException - If an I/O error occurs.