Skip to content

Commit

Permalink
Added attribute declarations
Browse files Browse the repository at this point in the history
  • Loading branch information
oozcitak committed Dec 21, 2013
1 parent 7e7bfda commit ca68f1d
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 7 deletions.
65 changes: 65 additions & 0 deletions src/XMLDTDAttList.coffee
@@ -0,0 +1,65 @@
_ = require 'underscore'

# Represents an attribute list
module.exports = class XMLDTDAttList


# Initializes a new instance of `XMLDTDAttList`
#
# `parent` the parent `XMLDocType` element
# `elementName` the name of the element containing this attribute
# `attributeName` attribute name
# `attributeType` type of the attribute (defaults to CDATA)
# `defaultValueType` default value type (either #REQUIRED, #IMPLIED, #FIXED or
# #DEFAULT) (defaults to #IMPLIED)
# `defaultValue` default value of the attribute
# (only used for #FIXED or #DEFAULT)
constructor: (parent, elementName, attributeName, attributeType, defaultValueType, defaultValue) ->
@stringify = parent.stringify

if not elementName?
throw new Error "Missing DTD element name"
if not attributeName?
throw new Error "Missing DTD attribute name"
if not attributeType
attributeType = 'CDATA'
if not defaultValueType
defaultValueType = '#IMPLIED'
if defaultValueType.indexOf('#') != 0
defaultValueType = '#' + defaultValueType
if not defaultValueType.match /^(#REQUIRED|#IMPLIED|#FIXED|#DEFAULT)$/
throw new Error "Invalid default value type; expected: #REQUIRED, #IMPLIED, #FIXED or #DEFAULT"
if defaultValue and not defaultValueType.match /^(#FIXED|#DEFAULT)$/
throw new Error "Default value only applies to #FIXED or #DEFAULT"

@elementName = @stringify.eleName elementName
@attributeName = @stringify.attName attributeName
@attributeType = @stringify.dtdAttType attributeType
@defaultValue = @stringify.dtdAttDefault defaultValue
@defaultValueType = defaultValueType

# Converts the XML fragment to string
#
# `options.pretty` pretty prints the result
# `options.indent` indentation for pretty print
# `options.newline` newline sequence for pretty print
toString: (options, level) ->
pretty = options?.pretty or false
indent = options?.indent or ' '
newline = options?.newline or '\n'
level or= 0

space = new Array(level).join(indent)

r = ''

r += space if pretty

r += '<!ATTLIST ' + @elementName + ' ' + @attributeName + ' ' + @attributeType
r += ' ' + @defaultValueType if @defaultValueType != '#DEFAULT'
r += ' "' + @defaultValue + '"' if @defaultValue
r += '>'

r += newline if pretty

return r
6 changes: 3 additions & 3 deletions src/XMLDTDElement.coffee
Expand Up @@ -19,7 +19,7 @@ module.exports = class XMLDTDElement
if _.isArray value
value = '(' + value.join(',') + ')'

@name = @stringify.dtdElementName name
@name = @stringify.eleName name
@value = @stringify.dtdElementValue value


Expand All @@ -37,10 +37,10 @@ module.exports = class XMLDTDElement
space = new Array(level).join(indent)

r = ''

r += space if pretty

r = '<!ELEMENT ' + @name + ' ' + @value + '>'
r += '<!ELEMENT ' + @name + ' ' + @value + '>'

r += newline if pretty

Expand Down
19 changes: 19 additions & 0 deletions src/XMLDocType.coffee
Expand Up @@ -36,6 +36,22 @@ module.exports = class XMLDocType
return @


# Creates an attribute declaration
#
# `elementName` the name of the element containing this attribute
# `attributeName` attribute name
# `attributeType` type of the attribute (defaults to CDATA)
# `defaultValueType` default value type (either #REQUIRED, #IMPLIED, #FIXED or
# #DEFAULT) (defaults to #IMPLIED)
# `defaultValue` default value of the attribute
# (only used for #FIXED or #DEFAULT)
attList: (elementName, attributeName, attributeType, defaultValueType, defaultValue) ->
XMLDTDAttList = require './XMLDTDAttList'
child = new XMLDTDAttList @, elementName, attributeName, attributeType, defaultValueType, defaultValue
@children.push child
return @


# Gets the root node
root: () ->
@parent.root()
Expand Down Expand Up @@ -90,4 +106,7 @@ module.exports = class XMLDocType

# Aliases
ele: (name, value) -> @element name, value
att: (elementName, attributeName, attributeType, defaultValueType, defaultValue) ->
@attList elementName, attributeName, attributeType, defaultValueType, defaultValue
up: () -> @root()
doc: () -> @document()
6 changes: 4 additions & 2 deletions src/XMLStringifier.coffee
Expand Up @@ -58,10 +58,12 @@ module.exports = class XMLStringifier
'' + val or ''
xmlSysID: (val) ->
'' + val or ''
dtdElementName: (val) ->
'' + val or ''
dtdElementValue: (val) ->
'' + val or ''
dtdAttType: (val) ->
'' + val or ''
dtdAttDefault: (val) ->
if val? then '' + val or '' else val

# strings to match while converting from JS objects
convertAttKey: '@'
Expand Down
8 changes: 6 additions & 2 deletions test/doctype.coffee
Expand Up @@ -65,15 +65,19 @@ vows
topic: () ->
xmlbuilder.create('root')
.dtd({ sysID: 'hello.dtd' })
.ele('img', 'EMPTY')
.ele('node')
.ele('img', 'EMPTY')
.att('img', 'height', 'CDATA', '#REQUIRED')
.att('img', 'visible', '(yes|no)', '#DEFAULT', "yes")
.ele('node')
.root()
.ele('node').txt('test')

'resulting XML': (topic) ->
xml = '<?xml version="1.0"?>' +
'<!DOCTYPE root SYSTEM "hello.dtd" [' +
'<!ELEMENT img EMPTY>' +
'<!ATTLIST img height CDATA #REQUIRED>' +
'<!ATTLIST img visible (yes|no) "yes">' +
'<!ELEMENT node (#PCDATA)>' +
']>' +
'<root><node>test</node></root>'
Expand Down

0 comments on commit ca68f1d

Please sign in to comment.