<@doc hierarchy="GMLDOM"> Provides a buffer for writing formatted output files The CodeWriter object is typically used to create a package of formatted files according to: Create a new CodeWriter for the files package Set the desired formatting style using the various CodeWriter settings Add new files and folders using the @createFile and @createFolder methods Add content to the current file using the various addXXX, beginXXX and endXXX methods Content is usually written serially to the last created file. To write in random access order, use the @getBookmark, @moveToBookmark and @moveToFile methods To report errors resp. warnings to the errors log while writing, use the @error resp. @warning methods Once all content has been written, call the @serialize method Use the @getRootFolder, @getSubFiles, or @getAllFiles to navigate through the serialized files Use the @getFileText or @getFileXml to retrieve the content of the serialized files In addition, the CodeWriter implements also the TreeProvider interface, and thus can be bound directly to the @lib:TreeView component (c) SAP AG 2003-2006. All rights reserved. /////////////////////////////////////////////////////////////////////// // CLASS HEADER Class CodeWriter inherit gml:Object; <@doc group="1. General"> CodeWriter constructor The package name The settings object (for overriding default CodeWriter settings) constructor(name, settings) this.xmlHeader = '<'+'?xml version="1.0" encoding="UTF-8"?'+'>'; this.cdataChar1 = '<'+'![CDATA['; this.cdataChar2 = ']]'+'>'; if (settings) { for (var k in settings) { if (k in this) this[k] = settings[k]; } } var root = {id:'0', name:name||'', path:this.pathChar, caption:name||'', type:'folder', isFolder:true, size:0, icon:'#URL[dev~res:skins.neutral.fileicons.pkg.gif]', order:0, files:[]}; this.currentBlock = null; this.currentFile = root; this.rootFile = root; this.filesIndex[root.path] = root; this.filesList.push(root); this.filesCount++; end <@struct name="GML_FILE"> Represents a @CodeWriter! file The GML_FILE structure is made of: Name | Type | Description id | String | The file id name | String | The file name path | String | The file absolute path type | String | The file type (extension) caption | String | The file display caption hint | String | The file short description icon | String | The file icon text | String | The file text (available after the file has been serialized) xml | XmlDocument | The file xml document (applies only to xml files; available after the file has been serialized) isFolder | Boolean | Indicates whether the file is a folder (readonly) isCode | Boolean | Indicates whether the file is a code file (readonly) isXml | Boolean | Indicates whether the file is an xml file (readonly) isImage | Boolean | Indicates whether the file is an image file (readonly) <@struct name="GML_ERROR"> Represents a @CodeWriter! error The GML_ERROR structure is made of: Name | Type | Description type | String | The error type (E=Error, W=Warning) msg | String | The error message obj | @gml:GmlObject | Optional. The GML element associated with the error unit | @gml:GmlObject | Optional. The GML unit that contains the error /////////////////////////////////////////////////////////////////////// // PROPERTIES <@doc scope="private">Gets the current file block readonly property currentBlock = null; <@doc scope="private">Gets the current file readonly property currentFile = null; <@doc scope="private">Gets the root folder readonly property rootFile = null; <@doc scope="private">Gets the list of all folders/files readonly property filesList = []; <@doc scope="private">Gets the index of all folders/files readonly property filesIndex = {}; <@doc scope="private">Gets the collection of all accumulated errors/warnings, indexed by unique key readonly property allErrors = {}; <@doc scope="private">Gets the collection of all result links, indexed by unique key readonly property allLinks = {}; <@doc scope="private">Gets the collection of all accumulated errors/warnings, grouped by unit Id readonly property unitErrors = {}; <@doc scope="private">Gets the collection of all accumulated links, grouped by DC Id readonly property dcLinks = {}; <@doc scope="private">Gets the collection of all accumulated info readonly property allInfo = {}; <@doc scope="private">Gets the grp sp readonly property grpSp = '#'; /////////////////////////////////////////////////////////////////////// // FILE HANDLING <@doc group="2. File Writing"> Creates a new folder The parent folder in which the new folder should be added The new folder name A short description of the new folder Display icon for the new folder. If omitted, the default folder icon will be used A numeric value used for controlling the display order of the new folder within its parent folder The newly created folder method createFolder(folder, name, hint, icon, order) return this.createFile(folder, name, 'folder', hint, icon, order); end <@doc> Creates a new file under the specified folder The parent folder in which the new file should be added The new file name The new file type (e.g., folder, txt, js, java, htm, xml, etc.) A short description of the new file Display icon for the new file. If omitted, the icon will be derived from the file's type A numeric value used for controlling the display order of the new file within its parent folder The newly created file method createFile(folder, name, type, hint, icon, order) type = LOWER(type); if (typeof folder == 'string' && folder.slice(-1) != this.pathChar) { folder += this.pathChar; } folder = this.getFile(folder); if (!folder || !folder.isFolder) { this.writerError('Bad or missing folder "'+folder+'"'); return null; } if(!type) type = 'unknown'; if(!icon && '|as|gif|html|ivu|java|jpg|js|jsp|mxml|pag|pkg|png|properties|swf|xgl|xlf|xml|folder|file|tgl|unknown|'.indexOf('|'+type+'|')<0){ if(type == 'folder')icon = '#URL[dev~res:skins.neutral.fileicons.folder.gif]'; else icon = '#URL[dev~res:skins.neutral.fileicons.file.gif]'; } var file = { id: this.filesList.length+'', name: name, type: type, hint: hint||'', icon: icon || '#URL[dev~res:skins.neutral.fileicons.%type%.gif]', size: 0, order: LIMIT(-99, INT(order), +99) } file.caption = name; if (type == 'folder') { file.isFolder = true; file.path = folder.path+name+this.pathChar; file.files = []; file.order += 100; } else { file.name = name+'.'+type; file.path = folder.path+file.name; file.linesCount = 0; file.rootBlock = this.createBlock(); file.rootBlock.file = file; file.order += 300; if ('|xml|xsl|xsd|wsdl|htm|html|htc|asp|jsp|mxml|xaml|xgl|gml|xlf|tgl|'.indexOf('|'+type+'|') >= 0) { file.isXml = true; } else if ('|java|js|as|gs|c|cpp|cs|'.indexOf('|'+type+'|') >= 0) { file.isCode = true; file.rootBlock.isCode = true; } else if ('|gif|jpg|png|'.indexOf('|'+type+'|') >= 0) { file.isImage = true; } } if (file.path in this.filesIndex) { this.writerError('File "'+file.path+'" already exists'); return null; } file.order = ('000'+file.order).slice(-3) + LOWER(name); file.parent = folder; folder.files.push(file); folder.size++; this.filesList.push(file); this.filesIndex[file.path] = file; this.currentFile = file; this.currentBlock = file.rootBlock || null; return file; end <@doc> Removes the specified file/folder The file/folder to remove When a folder is removed then its contained files/folders are removed as well (recursively) method removeFile(path) var file=this.getFile(arguments[0], arguments[1]); if (!file) return; if (file.isFolder) { for (var i=0, F=file.files, len=F.length; i Gets a bookmark object that represents the current position of the CodeWriter cursor The bookmark object method getBookmark() return this.currentBlock; end <@doc> Moves the CodeWriter cursor to the end of the specified file Absolute path name The absolute path name (including file name) Relative path name The parent folder The file name relative to the parent folder (including extension) File handle The file handle method moveToFile(path) var file = this.getFile.apply(this, arguments); if (!file) { this.writerError('Bad or missing file "'+path+'"'); return null; } this.currentFile = file; this.currentBlock = file.rootBlock || null; end <@doc> Moves the CodeWriter cursor to a previously saved bookmark The file bookmark to open method moveToBookmark(bookmark) if (!bookmark || !bookmark.file) { this.writerError('Bad or missing bookmark'); return null; } var file=bookmark.file; this.currentFile = file; this.currentBlock = bookmark; end <@doc> Serializes all files in the CodeWriter The CodeWriter must be serialized to make the files contents available in text representation. This method is typically invoked after the last write operation on the CodeWriter. method serialize() var files = this.filesIndex; for (var k in files) { this.serializeFile(files[k]); } end <@doc scope="private"> Serializes the given file method serializeFile(file) if (file.isFolder) return; file.text = this.serializeBlock(file.rootBlock); end <@doc scope="private"> Serializes the given block method serializeBlock(block) var len=block.length, nl=this.newlineChar; if (len == 0) return ''; if (!block.isNested) return block.join(nl); for (var i=0; i Adds one or more lines to the end of the current block The lines to add (if omitted, an empty line will be added) method add() var block=this.currentBlock; if (!block) { this.writerError('No current block for writing "'+line+'"'); return; } var len=arguments.length; if (len == 0) { this.addLine(block); } else { for (var i=0; i Begins a new block under the current block The block header line The new block Upon completion the current block is set to the newly created block A call to @beginBlock must be subsequently followed by a matching call to @endBlock method beginBlock(head, tail, type, isCode) var parent=this.currentBlock; if (parent) { var block = this.createBlock(parent, type||'', head, tail); block.isCode = isCode; this.currentBlock = block; return this.currentBlock; } else { this.writerError('No current block for writing "'+line+'"'); return null; } end <@doc also="@beginBlock"> Ends the current block The parent block (parent of the closed block) Upon completion the current block is set to the parent block A call to @endBlock should match a previous call to @beginBlock method endBlock(type) var block=this.currentBlock, type=type||''; if (!block) { this.writerError('No current block to close'); return null; } else if (!block.parent) { this.writerError('Top-level block cannot be closed'); return null; } else if (type !== block.type) { this.writerError('End "'+type+'" does not match Begin "'+block.type+'"'); return null; } else { this.currentBlock = block.parent; return this.currentBlock; } end <@doc scope="private"> Creates a new and empty block method createBlock(parent, type, head, tail) var block=[]; if (parent) { block.file = parent.file; block.parent = parent; block.indent = this.getIndent(parent, (this.indentBlockContents ? +1 : 0)); parent.isNested = true; parent.push(block); } else { block.parent = null; block.indent = ''; } block.type = type || ''; block.head = head || ''; block.tail = tail || ''; return block; end <@doc scope="private"> Adds a line to the end of the given block method addLine(block, line) block.push(line || ''); block.file.linesCount++; end <@doc scope="private"> Gets an indentation string for the given block plus/minus a specified offset method getIndent(block, offset) if (!block) { return ''; } else if (!offset) { return block.indent; } else if (offset < 0) { return block.indent.slice(0, offset*this.tabSize); } else { return block.indent + FILL(' ', offset*this.tabSize); } end /////////////////////////////////////////////////////////////////////// // IF/ELSE BLOCK <@doc also="@beginElse @endIf" group="4. Program Output (code)"> Begins an ~if block under the current block The ~if condition The new ~if block A call to @beginIf must be subsequently followed by a matching call to @endIf Between the @beginIf and matching @endIf calls, the @beginElse method can be called zero or more times Sample
source:
out.beginIf('a < 0');
out.add('print("negative")');
out.beginElse('a > 0');
out.add('print("positive")');
out.beginElse();
out.add('print("zero")');
out.endIf();

output:
if (a < 0) {
   print("negative");
} else if (a < 10) {
   print("positive");
} else if (a < 10) {
   print("zero");
}
method beginIf(cond) var head = 'if ('+(cond||'')+')'; var tail = ''; var block = this.beginBlock(head, tail, 'if', true); if (this.compactOneLineBlocks) { block.elseList = []; } return block; end <@doc also="@beginIf @endIf"> Adds an ~else clause to an ~if block The ~else condition The @beginElse method must be called between a matching pair of @beginIf and @endIf calls. The ~cond parameter is required in all but the last @beginElse call within the same ~if block. method beginElse(cond) var block=this.currentBlock; if (!block || block.type != 'if') { this.writerError('Else statment is out of If block'); } else { var nl=this.newlineChar, indent=this.getIndent(block,-1); var line = '}' + (this.compactBlockClauses ? ' ' : nl+indent) + 'else'; if (cond) line += ' if ('+cond+')'; line += (this.compactBlockBraces ? ' {' : nl+indent+'{'); this.addLine(block, indent+line); if (this.compactOneLineBlocks) { block.elseList[block.length-1] = true; } } end <@doc also="@beginIf @beginElse"> Ends the current ~if block The parent block (parent of the closed ~if block) A call to @endIf should match a previous call to @beginIf method endIf() var block=this.currentBlock; var parent=this.endBlock('if'); if (this.compactOneLineBlocks && !block.isNested) { // compact if/else clauses into one line, iff all clauses are exactly one-line var A=block.elseList, B=block, len=B.length, compact=true, nl=this.newlineChar; for (var i=0; i= 0 || (i>0 && !A[i-1]))) { compact=false; break; } } if (compact) { for (var i=0; i Begins a ~switch block under the current block The ~switch expression The new ~switch block A call to @beginSwitch must be subsequently followed by a matching call to @endSwitch Between the @beginSwitch and matching @endSwitch calls, the @beginCase method can be called one or more times Sample
source:
out.beginSwitch('type');
out.beginCase('A');
out.add('break;');
out.add();
out.beginCase('B');
out.add('break;');
out.add();
out.beginCase();
out.add('break;');
out.endSwitch();

output:
switch (type) {
   case 'A':
      break;

   case 'B':
      break;

   default:
      break;
}
method beginSwitch(expr) var head = 'switch ('+(expr||'')+')'; var tail = ''; var block = this.beginBlock(head, tail, 'switch', true); if (this.indentCaseContents) block.indent = this.getIndent(block,+1); return block; end <@doc also="@beginSwitch @endSwitch"> Adds a ~case clause to a ~switch block The ~case condition (if omitted, the ~default case will be added) The @beginCase method must be called between a matching pair of @beginSwitch and @endSwitch calls. The ~cond parameter is required in all but the last @beginCase call within the same ~switch block. method beginCase(cond) var block=this.currentBlock; if (!block || block.type != 'switch') { this.writerError('Case statment must be added to a Switch block'); } else { if (cond) { if (typeof cond == 'string') cond = QUOTE(cond); var line = 'case '+cond+':'; } else { var line = 'default:'; } if (this.insertCaseSpacing && block.length > 0) { this.addLine(block); } if (this.indentCaseContents) { this.addLine(block, this.getIndent(block,-1) + line); } else { this.addLine(block, this.getIndent(block) + line); } } end <@doc also="@beginSwitch @beginCase"> Ends the current ~switch block The parent block (parent of the closed ~switch block) A call to @endSwitch should match a previous call to @beginSwitch method endSwitch() return this.endBlock('switch'); end /////////////////////////////////////////////////////////////////////// // FOR BLOCK <@doc also="@endFor"> Begins a ~for block under the current block The ~for condition The new ~for block A call to @beginFor must be subsequently followed by a matching call to @endFor Sample
source:
out.beginFor('var i=0; i<10; i++');
out.add('print(i);');
out.endFor();

output:
for (var i=0; i<10; i++) {
   print(i);
}
method beginFor(cond) var head = 'for ('+(cond||'')+')'; var tail = ''; return this.beginBlock(head, tail, 'for', true); end <@doc also="@beginFor"> Ends the current ~for block The parent block (parent of the closed ~for block) A call to @endFor should match a previous call to @beginFor method endFor() return this.endBlock('for'); end /////////////////////////////////////////////////////////////////////// // WHILE BLOCK <@doc also="@endWhile"> Begins a ~while block under the current block The ~while condition The new ~while block A call to @beginWhile must be subsequently followed by a matching call to @endWhile Sample
source:
out.beginWhile('i < 10');
out.add('print(i);');
out.endWhile();

output:
while (i < 10) {
   print(i);
}
method beginWhile(cond) var head = 'while ('+(cond||'')+')'; var tail = ''; return this.beginBlock(head, tail, 'while', true); end <@doc also="@beginWhile"> Ends the current ~while block The parent block (parent of the closed ~while block) A call to @endWhile should match a previous call to @beginWhile method endWhile() return this.endBlock('while'); end /////////////////////////////////////////////////////////////////////// // DO BLOCK <@doc also="@endDo"> Begins a ~do block under the current block The ~do condition The new ~do block A call to @beginDo must be subsequently followed by a matching call to @endDo Sample
source:
out.beginDo('i < 10');
out.add('print(i);');
out.endDo();

output:
do {
   print(i);
} while (i < 10);
method beginDo(cond) var head = 'do'; var tail = 'while ('+(cond||'')+');'; return this.beginBlock(head, tail, 'do', true); end <@doc also="@beginDo"> Ends the current ~do block The parent block (parent of the closed ~do block) A call to @endDo should match a previous call to @beginDo method endDo() return this.endBlock('do'); end /////////////////////////////////////////////////////////////////////// // TRY/CATCH BLOCK <@doc also="@beginCatch @endTry"> Begins a ~try block under the current block The new ~try block A call to @beginTry must be subsequently followed by a matching call to @endTry Between the @beginTry and matching @endTry calls, the @beginCatch method can be called one or more times Sample
source:
out.beginTry();
out.add('...');
out.beginCatch('e');
out.add('...');
out.endTry();

output:
try {
   ...
} catch (e) {
   ...
}
method beginTry() var head = 'try'; var tail = ''; return this.beginBlock(head, tail, 'try', true); end <@doc also="@beginTry @endTry"> Adds a ~catch clause to a ~try block The ~catch expression The @beginCatch method must be called between a matching pair of @beginTry and @endTry calls. method beginCatch(expr) var block=this.currentBlock; if (!block || block.type != 'try') { this.writerError('Catch statement is out of Try block'); } else { var nl=this.newlineChar, indent=this.getIndent(block,-1); var line = '} ' + (this.compactBlockClauses ? '' : nl+indent) + 'catch('+expr+')'; line += (this.compactBlockBraces ? ' {' : nl+indent+'{'); this.addLine(block, indent + line); } end <@doc also="@beginTry @beginCatch"> Ends the current ~try block The parent block (parent of the closed ~try block) A call to @endTry should match a previous call to @beginTry method endTry() return this.endBlock('try'); end /////////////////////////////////////////////////////////////////////// // COMMENT BLOCK <@doc> Adds a single-line comment at the end of the current block The comment text Indicates whether to generate an emphasized comment The comment delimiters are determined automatically based on the containing block Sample
source:
out.beginIf('a < 100');
out.addComment('a is less than 100');
out.endIf();

output:
if (a < 100) {
   // a is less than 100
}
source:
out.beginTag('test', {max:100});
out.addComment('a is less than 100');
out.endTag();

output:
<test max="100">
   <!-- a is less than 100 -->
</test>
method addComment(text, emphasized) var block=this.currentBlock, text=text||''; if (!block) return; var indent = this.getIndent(block); if (block.type == 'tag') { if (emphasized) { var dash = FILL('=', 80); var text = ' '+TRIM(text)+' '; var len1 = text.length; var len2 = FLOOR((80-len1)/2); text = dash.slice(0,len2) + text + dash.slice(0, 80-len1-len2); } text = indent+'<'+'!-- '+text+' --'+'>'; } else { text = (emphasized ? indent+FILL('/',50)+this.newlineChar : '') + (indent+'/'+'/ '+text); } this.addLine(block, text); end /////////////////////////////////////////////////////////////////////// // XML TAG <@doc also="@endTag" group="5. Markup Output (xml)"> Begins a ~tag (xml) block under the current block The tag name The tag attributes The new ~tag block A call to @beginTag must be subsequently followed by a matching call to @endTag Sample
source:
out.beginTag('table', {id:"TBL1", width:100, height:100});
...
out.endTag();

output:
<table id="TBL1" width="100" height="100">
   ...
</table>
method beginTag(name, attrs) var head = '<'+name; var tail = ''; if (attrs) { for (var k in attrs) head += ' '+k+'="'+ESCAPEXML(attrs[k])+'"'; } return this.beginBlock(head+'>', tail, 'tag', false); end <@doc also="@addTag"> Adds a single-line ~tag (xml) at the end of the current block The tag name The tag attributes The tag text Sample
source:
out.addTag('input', {id:"F1", value:100});

output:
<input id="F1" value="100"/>
method addTag(name, attrs, text) var line = '<'+name; if (attrs) { for (var k in attrs) line += ' '+k+'="'+ESCAPEXML(attrs[k])+'"'; } if (text) line += '>'+ESCAPEXML(text)+''; else line += '/>'; return this.add(line); end <@doc also="@beginTag"> Ends the current ~tag (xml) block The parent block (parent of the closed ~tag block) A call to @endTag should match a previous call to @beginTag method endTag() return this.endBlock('tag'); end /////////////////////////////////////////////////////////////////////// // XML CDATA <@doc also="@endCData"> Begins a ~cdata (xml) block under the current block The cdata tag name The cdata tag attributes The new ~cdata block A call to @beginCData must be subsequently followed by a matching call to @endCData Sample
source:
out.beginCData('script', {language:"JavaScript"});
out.add('var a=0;');
out.add('a += 1;');
out.endCData();

output:
<script language="JavaScript"><![CDATA[
   var a=0;
   a = a+1;
]]></script>
method beginCData(name, attrs) var head = '<'+name; var tail = this.cdataChar2+''; if (attrs) { for (var k in attrs) head += ' '+k+'="'+ESCAPEXML(attrs[k])+'"'; } head += '>'+this.cdataChar1; return this.beginBlock(head, tail, 'cdata', false); end <@doc also="@beginCData"> Ends the current ~cdata (xml) block The parent block (parent of the closed ~cdata block) A call to @endCData should match a previous call to @beginCData method endCData() return this.endBlock('cdata'); end /////////////////////////////////////////////////////////////////////// // FILE READING <@doc group="6. File Reading">Gets the total number of folders/files readonly property filesCount = 0; <@doc> Gets a specified file/folder Absolute path name The absolute path name (including file name) Relative path name The parent folder The file name relative to the parent folder (including extension) File handle The file handle The requested file method getFile(path) switch (arguments.length) { case 0: return this.rootFile; case 1: var path=arguments[0]; if (!path) return this.rootFile; switch (typeof path) { case 'string': if (path.charAt(0) != this.pathChar) path = this.pathChar+path; return this.filesIndex[path] || null; case 'number': return this.filesList[path] || null; case 'object': return (('id' in path) && this.filesList[POS(path.id)] == path) ? path : null; } return null; case 2: var folder=this.getFile(arguments[0]), name=arguments[1]||''; return folder && this.getFile(folder.path+name) || null; } end <@doc> Gets the text contents of a specified file The file to fetch The requested file's text contents The CodeWriter must be serialized prior to using this method method getFileText(path) var file=this.getFile(arguments[0], arguments[1]); return file && file.text || ''; end <@doc> Gets the xml contents of a specified file The file to fetch The requested file's xml contents The CodeWriter must be serialized prior to using this method method getFileXml(path) var file=this.getFile(arguments[0], arguments[1]); if (!file || !file.isXml || !file.text) return null; if (file.xml || file.xmlError) return file.xml; var xml = PARSEXML(file.text); var err = CHECKXML(xml); if (err) { file.xml = null; file.xmlError = err; this.writerError('XML parsing error: '+file.xmlError); return null; } else { file.xml = xml; file.xmlError = null; return xml; } end <@doc> Gets statistics about the specified file/folder The file/folder to query The requested statistics The statistics are returned in an object with the following fields: {bytesCount:Number, filesCount:Number, foldersCount:Number} method getFileStatistics(path) var file = this.getFile(arguments[0], arguments[1]); var stats = {bytesCount:0, filesCount:0, foldersCount:0}; if (file) getStats(file); return stats; function getStats(file) { if (file.isFolder) { stats.foldersCount += 1; for (var i=0, F=file.files, len=F.length; i Gets the root folder The root folder method getRootFolder() return this.rootFile; end <@doc> Gets the list of all files/folders in the CodeWriter The files list method getAllFiles() return this.filesList; end <@doc> Gets the list of files/folders under a given folder The folder to fetch The files list method getSubFiles(path) var folder = this.getFile(arguments[0], arguments[1]); if (!folder || !folder.isFolder) return null; return folder.files; end <@doc scope="private"> Gets tree root (part of TreeProvider interface) method getRoot() return this.rootFile; end <@doc scope="private"> Gets tree node (part of TreeProvider interface) method getNode(id) return this.getFile(POS(id)); end <@doc scope="private"> Gets tree child nodes (part of TreeProvider interface) method getChildNodes(id) var file = this.getFile(POS(id)); if (!file || !file.isFolder) return []; SORT(file.files, 'order'); return file.files; end /////////////////////////////////////////////////////////////////////// // ERRORS HANDLING <@doc group="7. Errors Reporting">Gets the number of accumulated errors readonly property errorsCount = 0; <@doc>Gets the number of accumulated warnings readonly property warningsCount = 0; <@doc> Reports an error The error message The GML object associated with the error Optional hint about the error method error(msg, obj, hint) if (!msg) return; this.addErrorEntry({type:'E', msg:msg, obj:obj, hint:hint}, obj); this.errorsCount++; end <@doc> Reports a warning The warning message The GML object associated with the warning Optional hint about the warning method warning(msg, obj, hint) if (!msg) return; this.addErrorEntry({type:'W', msg:msg, obj:obj, hint:hint}, obj); this.warningsCount++; end <@doc scope="private"> Reports a CodeWriter error The error message method writerError(msg, obj) if (!msg) return; var file=this.currentFile, hint=''; if (file) { hint = 'near line '+file.linesCount+' in file '+file.path; } this.addErrorEntry({type:'E', msg:msg, hint:hint}); this.errorsCount++; end <@doc scope="private"> Adds an error object to the errors index method addErrorEntry(err, obj) var unit=null; if (ISA(obj, $ENV.model.unitClassifier)) { unit = obj; } else if (ISA(obj, $ENV.model.elementClassifier)) { unit = obj.unit; } if (unit) err.unit = unit; var grp = unit ? unit.id : '$'; var key = UPPER(unit ? grp+'$' : '$') + UPPER(obj? obj.id+'$' : '$') + err.msg; if (key in this.allErrors) return; this.allErrors[key] = err; if (!(grp in this.unitErrors)) { var E=[]; if (unit) E.title = unit.name; else E.title = 'General'; this.unitErrors[grp] = E; } this.unitErrors[grp].push(err); end <@doc> Gets the collection of all accumulated errors/warnings The errors collection method getAllErrors() return this.allErrors; end <@doc> Gets the collection of all accumulated errors, grouped by unit/warnings The errors collection method getErrorsByUnit() return this.unitErrors; end method getErrorsOfUnit(unit) var allErrors = this.unitErrors; if(unit && (unit.id in allErrors)) return allErrors[unit.id]; return null; end <@doc> Paints error/warning indicators next to all associated elements in the current diagram method paintErrorFlags() // TODO end <@doc> Erases any error/warning indicators in the current diagram method eraseErrorFlags() // TODO end /////////////////////////////////////////////////////////////////////// // LINKS HANDLING <@doc group="8. Links Reporting">Gets the number of links readonly property linksCount = 0; <@doc> Reports a link The link message The link address Optional hint about the link method link(grp,title, appName, link, provider, hint,fullClassName) if (!appName) return; this.addLinkEntry({type:'L', grp:grp, title: title, msg:appName, link:link, ruleExtenders:provider, hint:hint, fullClassName:fullClassName}); this.linksCount++; end <@doc scope="private"> Adds a link to the links index method addLinkEntry(obj) if(!obj)return; var grp = obj.grp || '$'; context = this; var key = UPPER(grp ? grp+'$' : '$') + UPPER(obj.msg || '' +'$'); this.allLinks[key] = obj; var newNode = getFullPath() newNode.push(obj); function getFullPath(){ var fullpath = null; if(obj.title)fullpath = obj.title.split('#'); if (!(grp in context.dcLinks)){ var L=[]; L.title = fullpath ? fullpath[0] : grp; context.dcLinks[grp] = L; } var node = context.dcLinks[grp]; if(fullpath){ for(var k=1 ; k < fullpath.length ; ++k){ var entry = fullpath[k] if(!node.subgrp)node.subgrp = {}; if(!node.subgrp[entry]){ var L = []; L.title = entry; node.subgrp[entry] = L; } node = node.subgrp[entry]; } } return node; } end <@doc> Gets the collection of all links The links collection method getAllLinks() return this.allLinks; end <@doc> Gets the collection of all accumulated links, grouped by DC The links collection method getLinksByDc() return this.dcLinks; end <@doc> Reports a link The info message Optional hint about the info method info(msg, hint) if (!msg) return; this.addInfoEntry({type:'I', msg:msg, hint:hint}); end <@doc scope="private"> Adds a info to the infos index method addInfoEntry(msg) if(!this.allInfo['I']) this.allInfo['I'] = []; this.allInfo['I'].push(msg); end <@doc> Gets the collection of all infos The infos collection method getAllInfos() return this.allInfo; end /////////////////////////////////////////////////////////////////////// // FORMAT SETTINGS <@doc group="9. Settings">Gets or sets the newline characters sequence property newlineChar = '\r\n'; <@doc>Gets or sets the tab size (number of spaces) property tabSize = 3; <@doc>Gets or sets the path separator character property pathChar = '\\'; <@doc scope="private">XML header characters sequence property xmlHeader = ''; <@doc scope="private">CDATA block start sequence property cdataChar1 = ''; <@doc scope="private">CDATA block end sequence property cdataChar2 = ''; <@doc>Indicates whether opening brace of a block is placed inline or on the next line Sample compactBlockBraces = true
if (a < 0) {
   print("negative");
}
compactBlockBraces = false
if (a < 0)
{
   print("negative");
}
property compactBlockBraces = true; <@doc>Indicates whether a block clause (e.g., Else, Catch, etc.) is placed inline or on the next line Sample compactBlockClauses = true and compactBlockBraces = true
if (a < 0) {
   print("negative");
} else {
   print("positive");
}
compactBlockClauses = false and compactBlockBraces = true
if (a < 0) {
   print("negative");
}
else {
   print("positive");
}
compactBlockClauses = false and compactBlockBraces = false
if (a < 0)
{
   print("negative");
}
else
{
   print("positive");
}
property compactBlockClauses = true; <@doc>Indicates whether one-line blocks are placed inline or enclosed in braces on separate lines Sample compactOneLineBlocks = true
if (a < 0) print("negative"); else print("positive");
compactOneLineBlocks = false
if (a < 0) {
   print("negative");
} else {
   print("positive");
}
property compactOneLineBlocks = false; <@doc>Indicates whether block contents are indented one level to the right of the block statement Sample indentBlockContents = true
function (a, b) {
   if (a < b) {
      print("a < b");
   } else {
      print("a > b");
   }
}
indentBlockContents = false
function (a, b) {
if (a < b) {
print("a < b");
} else {
print("a > b");
}
}
property indentBlockContents = true; <@doc>Indicates whether case contents are indented one level to the right of the case labels Sample indentCaseContents = true
switch (type) {
   case 'A':
      print("A");
      break;

   case 'B':
      print("B");
      break;

   default:
      print("X");
      break;
}
indentCaseContents = false
switch (type) {
   case 'A':
   print("A");
   break;

   case 'B':
   print("B");
   break;

   default:
   print("X");
   break;
}
property indentCaseContents = true; <@doc>Indicates whether to insert spacing between adjacent case clauses Sample insertCaseSpacing = true
switch (type) {
   case 'A':
      print("A");
      break;

   case 'B':
      print("B");
      break;

   default:
      print("X");
      break;
}
insertCaseSpacing = false
switch (type) {
   case 'A':
      print("A");
      break;
   case 'B':
      print("B");
      break;
   default:
      print("X");
      break;
}
property insertCaseSpacing = true; <@doc>Indicates whether statement terminators should be automatically added to the end of lines in code blocks Sample completeStatements = true
function (a,b) {
   var c;
   c = a + b;
   print(c);
}
completeStatements = false
function (a,b) {
   var c
   c = a + b
   print(c)
}
property completeStatements = true;