org.ikayzo.sdl
Class Tag

java.lang.Object
  extended by org.ikayzo.sdl.Tag
All Implemented Interfaces:
java.io.Serializable

public class Tag
extends java.lang.Object
implements java.io.Serializable

SDL (Simple Declarative Language) documents are made up of Tags. Tags contain

      a name (if not present, the name "content" is used)
      a namespace (optional)
      0 or more values (optional)
      0 or more attributes (optional)
      0 or more children (optional)
 

For the SDL code:

     size 4
     smoker false
 

Assuming this code is in a file called values.sdl, the values can be read using the following code (ignoring exceptions):

     Tag root = new Tag("root").read(new File("values.sdl"));
     int size = root.getChild("size").intValue();
     boolean smoker = root.getChild("smoker").booleanValue();
 

A tag is basically a data structure with a list of values, a map of attributes, and (if it has a body) child tags. In the example above, the "values.sdl" file is read into a tag called "root". It has two children (tags) called "size" and "smoker". Both these children have one value, no attributes, and no bodies.

SDL is often used for simple key-value mappings. To simplify things Tag has the methods getValue and setValue which operate on the first element in the values list. Also notice SDL understands types which are determined using type inference.

The example above used the simple format common in property files:

 name value
 

The full SDL tag format is:

 namespace:name value_list attribute_list {
     children_tags
 }
 

where value_list is zero or more space separated SDL literals and attribute_list is zero or more space separated (namespace:)key=value pairs. The name, namespace, and keys are SDL identifiers. Values are SDL literals. Namespace is optional for both tag names and attributes. Tag bodies are also optional. SDL identifiers begin with a unicode letter or an underscore (_) followed by zero or more unicode letters, numbers, underscores (_) and dashes (-).

SDL also supports anonymous tags which are assigned the name "content". An annoymous tag starts with a literal and is followed by zero or more additional literals and zero or more attributes. The examples section below demonstrates the use of anonymous tags.

Tags without bodies are terminated by a new line character (\n) and may be continue onto the next line by placing a backslash (\) at the end of the line. Tags may be nested to an arbitrary depth. SDL ignores all other white space characters between tokens. Although nested blocks are indented by convention, tabs have no significance in the language.

There are two ways to write String literals.

1. Starting and ending with double quotes ("). Double quotes, backslash characters (\), and new lines (\n) within this type of String literal must be escaped like so:

     file "C:\\folder\\file.txt"
     say "I said \"something\""
 

This type of String literal can be continued on the next line by placing a backslash (\) at the end of the line like so:

     line "this is a \
          long string of text"
 

White space before the first character in the second line will be ignored.

2. Starting and ending with a backquote (`). This type of string literal can only be ended with a second backquote (`). It is not necessary (or possible) to escape any type of character within a backquote string literal. This type of literal can also span lines. All white space is preserved including new lines.

Examples:

     file `C:\folder\file.txt`
     say `I said "something"`
     regex `\w+\.suite\(\)`
     long_line `This is
         a long line
         fee fi fo fum`
 

Note: SDL interprets new lines in `` String literals as a single new line character (\n) regarless of the platform.

Binary literals use base64 characters enclosed in square brackets ([]). The binary literal type can also span lines. White space is ignored.

Examples:

     key [sdf789GSfsb2+3324sf2] name="my key"
     image [
         R3df789GSfsb2edfSFSDF
         uikuikk2349GSfsb2edfS
         vFSDFR3df789GSfsb2edf
     ]
     upload from="ikayzo.com" data=[
         R3df789GSfsb2edfSFSDF
         uikuikk2349GSfsb2edfS
         vFSDFR3df789GSfsb2edf
     ]
 

SDL supports date, time span, and date/time literals. Data and Date/Time literals use a 24 hour clock (0-23). If a timezone is not specified, the default locale's timezone will be used.

Examples:

     # create a tag called "date" with a date value of Dec 5, 2005
     date 2005/12/05
     
     # various time span literals
     hours 03:00:00
     minutes 00:12:00
     seconds 00:00:42
     short_time 00:12:32.423 # 12 minutes, 32 seconds, 423 milliseconds
     long_time 30d:15:23:04.023 # 30 days, 15 hours, 23 mins, 4 secs, 23 millis 
     before -00:02:30 # 2 hours and 30 minutes ago
     
     # combine date and time to create a date_time literal
     in_japan 2005/12/05 14:12:23.345-JST    
 

SDL 1.0 has thirteen literal types (parenthesis indicate optional components)

     1. string (unicode) - examples: "hello" or `aloha`
     2. character (unicode) - example: '/'
            Note: \uXXXX style unicode escapes are not supported (or
            needed because sdl files are UTF8)
     3. integer (32 bits signed) - example: 123
     4. long integer (64 bits signed) - examples: 123L or 123l
     5. float (32 bits signed) - examples 123.43F 123.43f 
     6. double float (64 bits signed) - example: 123.43 or 123.43d or 123.43D
     7. decimal (128+ bits signed) - example: 123.44BD or 123.44bd
     8. boolean - examples: true or false or on or off
     9. date yyyy/mm/dd - example 2005/12/05
     10. date time yyyy/mm/dd hh:mm(:ss)(.xxx)(-ZONE)
            example - 2005/12/05 05:21:23.532-JST
            notes: uses a 24 hour clock (0-23), only hours and minutes are
                   mandatory
     11. time span using the format (d:)hh:mm:ss(.xxx)
            notes: if the day component is included it must be suffixed with
                   a lower case 'd'
            examples 12:14:42 (12 hours, 14 minutes, 42 seconds)
                     00:09:12 (9 minutes, 12 seconds)
                     00:00:01.023 (1 second, 23 milliseconds)
                     23d:05:21:23.532 (23 days, 5 hours, 21 minutes,
                         23 seconds, 532 milliseconds)
     12. binary [base64] exmaple - [sdf789GSfsb2+3324sf2]
     13. null
 

* Timezones must be specified using a valid time zone ID (ex. America/Los_Angeles), three letter abbreviation (ex. HST), or GMT(+/-)hh(:mm) formatted custom timezone (ex. GMT+02 or GMT+02:30)

Note: SDL 1.1 will likely add a reference literal type.

These types are designed to be portable across Java, .NET, and other popular platforms.

SDL supports four comment types.

  1. // single line comments identicle to those used in Java, C, etc. // style comments can occur anywhere in a line. All text after // up to the new line will be ignored.
  2. # property style comments. They work the same way as //
  3. -- separator comments useful for visually dividing content. They work the same way as //
  4. Slash star (/*) style multiline comments. These begin with a slash star and end with a star slash. Everything in between is ignored.

A example SDL file:

     # a tag having only a name
     my_tag
 
     # three tags acting as name value pairs
     first_name "Akiko"
     last_name "Johnson"
     height 68
     
     # a tag with a value list
     person "Akiko" "Johnson" 68
     
     # a tag with attributes
     person first_name="Akiko" last_name="Johnson" height=68
     
     # a tag with values and attributes
     person "Akiko" "Johnson" height=60
     
     # a tag with attributes using namespaces
     person name:first-name="Akiko" name:last-name="Johnson"
     
     # a tag with values, attributes, namespaces, and children
     my_namespace:person "Akiko" "Johnson" dimensions:height=68 {
         son "Nouhiro" "Johnson"
         daughter "Sabrina" "Johnson" location="Italy" {
             hobbies "swimming" "surfing"
             languages "English" "Italian"
             smoker false
         }
     }   
     
     ------------------------------------------------------------------
     // (notice the separator style comment above...)
     
     # a log entry
     #     note - this tag has two values (date_time and string) and an 
     #            attribute (error)
     entry 2005/11/23 10:14:23.253-GMT "Something bad happened" error=true
     
     # a long line
     mylist "something" "another" true "shoe" 2002/12/13 "rock" \
         "morestuff" "sink" "penny" 12:15:23.425
     
     # a long string
     text "this is a long rambling line of text with a continuation \
        and it keeps going and going..."
        
    # anonymous tag examples
    
    files {
        "/folder1/file.txt"
        "/file2.txt"
    }
        
    # To retrieve the files as a list of strings
    #
    #     List files = tag.getChild("files").getChildrenValues("content");
    # 
    # We us the name "content" because the files tag has two children, each of 
    # which are anonymous tags (values with no name.)  These tags are assigned
    # the name "content"
        
    matrix {
        1 2 3
        4 5 6
    }
    
    # To retrieve the values from the matrix (as a list of lists)
    #
    #     List rows = tag.getChild("matrix").getChildrenValues("content");
        
 

Example of getting the "location" attribute from the "daughter" tag above (ignoring exceptions)

      Tag root = new Tag("root").read("myfile.sdl");
      Tag daughter = root.getChild("daughter", true); // recursive search
      String location = daughter.getAttribute("location").toString();
  

SDL is normally stored in a file with the .sdl extension. These files should always be encoded using UTF8. SDL fully supports unicode in identifiers and literals.

Author:
Daniel Leuck
See Also:
Serialized Form

Constructor Summary
Tag(java.lang.String name)
          Creates an empty tag.
Tag(java.lang.String namespace, java.lang.String name)
          Creates an empty tag in the given namespace.
 
Method Summary
 void addChild(Tag child)
          Add a child to this Tag.
 void addValue(java.lang.Object value)
          Add a value to this Tag.
 boolean booleanValue()
          A convenience method that returns the first value as a boolean
 boolean equals(java.lang.Object o)
          Returns true if this tag (including all of its values, attributes, and children) is equivalent to the given tag.
 java.lang.Object getAttribute(java.lang.String key)
          Get the attribute value associated with the given key.
 java.util.Map<java.lang.String,java.lang.String> getAttributeNamespaces()
          Returns an immutable view of the map from attribute keys to their namespace.
 java.util.SortedMap<java.lang.String,java.lang.Object> getAttributes()
          Get an immutable view of the attributes.
 java.util.SortedMap<java.lang.String,java.lang.Object> getAttributesForNamespace(java.lang.String namespace)
          Returns an immutable view of all the attributes in the given namespace.
 Tag getChild(java.lang.String childName)
          Get the first child with the given name.
 Tag getChild(java.lang.String childName, boolean recursive)
          Get the first child with the given name, optionally using a recursive search.
 java.util.List<Tag> getChildren()
          Get all the children for this Tag
 java.util.List<Tag> getChildren(boolean recursively)
          Get all the children of this tag optionally recursing through all descendents.
 java.util.List<Tag> getChildren(java.lang.String childName)
          Get all children with the given name.
 java.util.List<Tag> getChildren(java.lang.String childName, boolean recursive)
          Get all the children with the given name (optionally searching descendants recursively)
 java.util.List<Tag> getChildrenForNamespace(java.lang.String namespace)
          Get all children in the given namespace.
 java.util.List<Tag> getChildrenForNamespace(java.lang.String namespace, boolean recursive)
          Get all the children in the given namespace (optionally searching descendants recursively)
 java.util.List getChildrenValues(java.lang.String name)
          Get the values for all children with the given name.
 java.lang.String getName()
          Get the name of this Tag.
 java.lang.String getNamespace()
          Returns the namespace.
 java.lang.Object getValue()
          A convenience method that returns the first value.
 java.util.List<java.lang.Object> getValues()
          Get all the values for this Tag.
 int hashCode()
           
 int intValue()
          A convenience method that returns the first value as an int
 Tag read(java.io.File file)
          Add all the tags specified in the given file to this Tag.
 Tag read(java.io.Reader reader)
          Add all the tags specified in the given Reader to this Tag.
 Tag read(java.lang.String text)
          Add all the tags specified in the given String to this Tag.
 Tag read(java.net.URL url)
          Add all the tags specified in the file at the given URL to this Tag.
 java.lang.Object removeAttribute(java.lang.String attributeKey)
          Remove the attribute value associated with the given key.
 boolean removeChild(Tag child)
          Remove a child from this Tag
 boolean removeValue(java.lang.Object value)
          Remove a value from this Tag.
 void setAttribute(java.lang.String key, java.lang.Object value)
          Set an attribute for this tag.
 void setAttribute(java.lang.String namespace, java.lang.String key, java.lang.Object value)
          Set an attribute in the given namespace for this tag.
 void setAttributes(java.util.Map<java.lang.String,java.lang.Object> attributes)
          Set all the attributes for this Tag in one operation.
 void setName(java.lang.String name)
          Sets the name of this Tag.
 void setNamespace(java.lang.String namespace)
          The namespace to set.
 void setValue(java.lang.Object value)
          A convenience method that sets the first value in the value list.
 void setValues(java.util.Collection values)
          Set the values for this tag.
 java.lang.String stringValue()
          A convenience method that returns the first value as a String
 java.lang.String toString()
          Get a String representation of this SDL Tag.
 java.lang.String toXMLString()
          Returns a string containing an XML representation of this tag.
 void write(java.io.File file)
          Write this tag out to the given file.
 void write(java.io.File file, boolean includeRoot)
          Write this tag out to the given file (optionally clipping the root.)
 void write(java.io.Writer writer, boolean includeRoot)
          Write this tag out to the given writer (optionally clipping the root.)
 
Methods inherited from class java.lang.Object
clone, finalize, getClass, notify, notifyAll, wait, wait, wait
 

Constructor Detail

Tag

public Tag(java.lang.String name)
Creates an empty tag.

Parameters:
name - The name of this tag
Throws:
java.lang.IllegalArgumentException - if the name is not a legal SDL identifier. See SDL.validateIdentifier(String)

Tag

public Tag(java.lang.String namespace,
           java.lang.String name)
Creates an empty tag in the given namespace. If the namespace is null it will be coerced to an empty String.

Parameters:
namespace - The namespace for this tag
name - The name of this tag
Throws:
java.lang.IllegalArgumentException - if the name is not a legal SDL identifier (see SDL.validateIdentifier(String)) or the namespace is non-blank and is not a legal SDL identifier.
Method Detail

addChild

public void addChild(Tag child)
Add a child to this Tag.

Parameters:
child - The child to add

removeChild

public boolean removeChild(Tag child)
Remove a child from this Tag

Parameters:
child - The child to remove
Returns:
true if the child exists and is removed

setValue

public void setValue(java.lang.Object value)
A convenience method that sets the first value in the value list. See addValue(Object) for legal types.

Parameters:
value - The value to be set.
Throws:
java.lang.IllegalArgumentException - if the value is not a legal SDL type

getValue

public java.lang.Object getValue()
A convenience method that returns the first value.

Returns:
The first value

stringValue

public java.lang.String stringValue()
A convenience method that returns the first value as a String

Returns:
The value assuming a String

booleanValue

public boolean booleanValue()
A convenience method that returns the first value as a boolean

Returns:
The value assuming a boolean

intValue

public int intValue()
A convenience method that returns the first value as an int

Returns:
The value assuming an int

getChild

public Tag getChild(java.lang.String childName)
Get the first child with the given name. The search is not recursive.

Parameters:
childName - The name of the child Tag
Returns:
The first child tag having the given name or null if no such child exists

getChild

public Tag getChild(java.lang.String childName,
                    boolean recursive)
Get the first child with the given name, optionally using a recursive search.

Parameters:
childName - The name of the child Tag
Returns:
The first child tag having the given name or null if no such child exists

getChildren

public java.util.List<Tag> getChildren(java.lang.String childName)
Get all children with the given name. The search is not recursive.

Parameters:
childName - The name of the children to fetch
Returns:
All the child tags having the given name

getChildren

public java.util.List<Tag> getChildren(java.lang.String childName,
                                       boolean recursive)
Get all the children with the given name (optionally searching descendants recursively)

Parameters:
childName - The name of the children to fetch
recursive - If true search children recursively in all child tags
Returns:
All the child tags having the given name

getChildrenForNamespace

public java.util.List<Tag> getChildrenForNamespace(java.lang.String namespace)
Get all children in the given namespace. The search is not recursive.

Parameters:
namespace - The namespace to search
Returns:
All the child tags in the given namespace

getChildrenForNamespace

public java.util.List<Tag> getChildrenForNamespace(java.lang.String namespace,
                                                   boolean recursive)
Get all the children in the given namespace (optionally searching descendants recursively)

Parameters:
namespace - The namespace of the children to fetch
recursive - If true search all descendents
Returns:
All the child tags in the given namespace

getChildrenValues

public java.util.List getChildrenValues(java.lang.String name)
Get the values for all children with the given name. If the child has more than one value, all the values will be added as a list. If the child has no value, null will be added. The search is not recursive.

Parameters:
name - The name of the children from which values are retrieved
Returns:
A list of values (or lists of values)

addValue

public void addValue(java.lang.Object value)
Add a value to this Tag. The allowable types are String, Number, Boolean, Character, byte[], Byte[] (coerced to byte[]), Calendar, Date (coerced to Calendar), and null. Passing any other type will result in an IllegalArgumentException.

Parameters:
value - The value to add
Throws:
java.lang.IllegalArgumentException - if the value is not a legal SDL type

removeValue

public boolean removeValue(java.lang.Object value)
Remove a value from this Tag.

Parameters:
value - The value to remove
Returns:
true If the value exists and is removed

getValues

public java.util.List<java.lang.Object> getValues()
Get all the values for this Tag.

Returns:
An immutable view of the values.

setValues

public void setValues(java.util.Collection values)
Set the values for this tag. See addValue(Object) for legal value types.

Parameters:
values - The new values
Throws:
java.lang.IllegalArgumentException - if the collection contains any values which are not legal SDL types

setAttribute

public void setAttribute(java.lang.String key,
                         java.lang.Object value)
Set an attribute for this tag. The allowable attribute value types are the same as those allowed for addValue(Object)

Parameters:
key - The attribute key
value - The attribute value
Throws:
java.lang.IllegalArgumentException - if the key is not a legal SDL identifier (see SDL.validateIdentifier(String)) or the value is not a legal SDL type.

setAttribute

public void setAttribute(java.lang.String namespace,
                         java.lang.String key,
                         java.lang.Object value)
Set an attribute in the given namespace for this tag. The allowable attribute value types are the same as those allowed for addValue(Object)

Parameters:
namespace - The namespace for this attribute
key - The attribute key
value - The attribute value
Throws:
java.lang.IllegalArgumentException - if the key is not a legal SDL identifier (see SDL.validateIdentifier(String)), or the namespace is non-blank and is not a legal SDL identifier, or the value is not a legal SDL type

getAttribute

public java.lang.Object getAttribute(java.lang.String key)
Get the attribute value associated with the given key.

Returns:
The value for the key if such a key exists

removeAttribute

public java.lang.Object removeAttribute(java.lang.String attributeKey)
Remove the attribute value associated with the given key.

Parameters:
attributeKey - The attribute key
Returns:
The value for the attribute key if the key exists

getAttributes

public java.util.SortedMap<java.lang.String,java.lang.Object> getAttributes()
Get an immutable view of the attributes.

Returns:
An immutable view of the attributes.

setAttributes

public void setAttributes(java.util.Map<java.lang.String,java.lang.Object> attributes)
Set all the attributes for this Tag in one operation. See addValue(Object) for allowable attribute value types.

Parameters:
attributes - The new attributes
Throws:
java.lang.IllegalArgumentException - if any key in the map is not a legal SDL identifier (see SDL.validateIdentifier(String)), or any value is not a legal SDL type

getAttributeNamespaces

public java.util.Map<java.lang.String,java.lang.String> getAttributeNamespaces()
Returns an immutable view of the map from attribute keys to their namespace. Keys not in a namespace will be mapped to an empty String.

Returns:
An immutable view of the namespace to attribute key map.

getAttributesForNamespace

public java.util.SortedMap<java.lang.String,java.lang.Object> getAttributesForNamespace(java.lang.String namespace)
Returns an immutable view of all the attributes in the given namespace.

Returns:
An immutable view of all the attributes in the given namespace.

getChildren

public java.util.List<Tag> getChildren()
Get all the children for this Tag

Returns:
An immutable view of the children.

getChildren

public java.util.List<Tag> getChildren(boolean recursively)
Get all the children of this tag optionally recursing through all descendents.

Parameters:
recursively - If true, recurse through all descendents
Returns:
An immutable view of the children

getName

public java.lang.String getName()
Get the name of this Tag.

Returns:
This Tag's name

setName

public void setName(java.lang.String name)
Sets the name of this Tag.

Parameters:
name - The name to set.
Throws:
java.lang.IllegalArgumentException - if the name is not a legal SDL identifier (see SDL.validateIdentifier(String))

getNamespace

public java.lang.String getNamespace()
Returns the namespace. Namespace is never null but may be empty.

Returns:
This Tag's namespace

setNamespace

public void setNamespace(java.lang.String namespace)
The namespace to set. null will be coerced to the empty string.

Parameters:
namespace - The namespace to set
Throws:
java.lang.IllegalArgumentException - if the namespace is non-blank and is not a legal SDL identifier (see SDL.validateIdentifier(String))

read

public Tag read(java.net.URL url)
         throws java.io.IOException,
                SDLParseException
Add all the tags specified in the file at the given URL to this Tag.

Parameters:
url - A UTF8 encoded .sdl file
Returns:
This tag after adding all the children read from the reader
Throws:
java.io.IOException - If there is an IO problem reading the source
ParseException - If the SDL input is malformed
SDLParseException

read

public Tag read(java.io.File file)
         throws java.io.IOException,
                SDLParseException
Add all the tags specified in the given file to this Tag.

Parameters:
file - A UTF8 encoded .sdl file
Returns:
This tag after adding all the children read from the reader
Throws:
java.io.IOException - If there is an IO problem reading the source
ParseException - If the SDL input is malformed
SDLParseException

read

public Tag read(java.lang.String text)
         throws SDLParseException
Add all the tags specified in the given String to this Tag.

Parameters:
text - An SDL string
Returns:
This tag after adding all the children read from the reader
Throws:
ParseException - If the SDL input is malformed
SDLParseException

read

public Tag read(java.io.Reader reader)
         throws java.io.IOException,
                SDLParseException
Add all the tags specified in the given Reader to this Tag.

Parameters:
reader - A reader containing SDL source
Returns:
This tag after adding all the children read from the reader
Throws:
java.io.IOException - If there is an IO problem reading the source
ParseException - If the SDL input is malformed
SDLParseException

write

public void write(java.io.File file)
           throws java.io.IOException
Write this tag out to the given file.

Parameters:
file - The file to which we will write the children of this tag.
Throws:
java.io.IOException - If there is an IO problem during the write operation

write

public void write(java.io.File file,
                  boolean includeRoot)
           throws java.io.IOException
Write this tag out to the given file (optionally clipping the root.)

Parameters:
file - The file to which we will write this tag
includeRoot - If true this tag will be included in the file as the root element, if false only the children will be written
Throws:
java.io.IOException - If there is an IO problem during the write operation

write

public void write(java.io.Writer writer,
                  boolean includeRoot)
           throws java.io.IOException
Write this tag out to the given writer (optionally clipping the root.)

Parameters:
writer - The writer to which we will write this tag
includeRoot - If true this tag will be written out as the root element, if false only the children will be written
Throws:
java.io.IOException - If there is an IO problem during the write operation

toString

public java.lang.String toString()
Get a String representation of this SDL Tag. This method returns a complete description of the Tag's state using SDL (i.e. the output can be parsed by read(String))

Overrides:
toString in class java.lang.Object
Returns:
A string representation of this tag using SDL

equals

public boolean equals(java.lang.Object o)
Returns true if this tag (including all of its values, attributes, and children) is equivalent to the given tag.

Overrides:
equals in class java.lang.Object
Returns:
true if the tags are equivalet

hashCode

public int hashCode()
Overrides:
hashCode in class java.lang.Object
Returns:
The hash (based on the output from toString())

toXMLString

public java.lang.String toXMLString()
Returns a string containing an XML representation of this tag. Values will be represented using _val0, _val1, etc.

Returns:
An XML String describing this Tag