Tuesday, April 17, 2007

Automate "save as" and file listings generation


Haven't you ever faced with a need to convert set of your documentation (say Visio diagrams) so that it supports previous version of the software if compared to the one you used to create it? It's often the case that we as a software company run a newer version of the software if compared to our customers.

One of the possible solution is to avoid the problem and mandate that a project team uses Visio 2002 (or Word 2000). Another option is to go file by file and open-save as previous version. I liked the third option the most - have a small script to automate it:

//*************************************************************
// WSH Script to automate Visio 2003 Save As Visio 2002 Process
//**************************************************************


var sPath = WScript.Arguments(0);

var sCurrentNesting = "\\";

var oVisio = WScript.CreateObject("Visio.Application");
var oFOS = WScript.CreateObject("Scripting.FileSystemObject");


var oFolder = oFOS.GetFolder(sPath);

iterate(oFolder);


function iterate(oFolder) {
var oFiles = new Enumerator(oFolder.Files);
for (; !oFiles.atEnd(); oFiles.moveNext()) {
var oFile = oFiles.item();
if (oFOS.GetExtensionName(oFile.Path) == "vsd") {
// WScript.Echo(oFile.Path);

oVisio.Visible = false;
oVisio.Documents.Open(oFile.Path)

// reset version to 2002
oVisio.ActiveDocument.Version = 393216;

oVisio.AlertResponse = 6; //Yes
oVisio.ActiveDocument.SaveAs(oFile.Path);
oVisio.AlertResponse = 0; // Default

oVisio.ActiveDocument.Close();
}

}

var sPreviousContext = sCurrentNesting;

var oSubFolders = new Enumerator(oFolder.SubFolders);
for (; !oSubFolders.atEnd(); oSubFolders.moveNext()) {
var oSubFolder = oSubFolders.item();
sCurrentNesting += oSubFolder.Name + "\\";

iterate(oSubFolder);

sCurrentNesting = sPreviousContext;
}
}

You can modify it to do the same thing for your Word or Excel files. Or may be do something else with them.

Yesterday I needed a list of files recursively from the current folder populated into Excel spreadsheet. Just have a bunch of documents to go through so decided to have a tracking sheet to make sure I don't miss anything and someone else after me has a categorized list with filtered out duplicates, etc. I remembered the small script I created a few years ago (the one presented above) and thought I would update it a little to generate a list for me. I ended up with a much simpler solution:

dir /a /b /s > filelisting.txt

:) you can make "Generate File Listing" a command in your explorer context menu. If you interested how - read here

Cheers

Sunday, April 15, 2007

Colorize code samples for your blog post


Problem Statement
CSS-based colorer?
Colorer on sourceforge.net
Back to CSS-based colorer
Wow, this is an SOA and AJAX :)
New behavior
Test First
Colorer-Bridge Implementation
P.S.

Problem Statement

When working on the article about my journey with WebDav technology I faced with a challenge to colorize code samples that I wanted to share. I wanted that the source code in my article be not only shadowed gray and printed with Courier font, but really presented the way you would expect it to be presented by an IDE.

I saw a few development forums doing this behind the scenes and I thought I wanted the same capability on my blog. The challenge is that my blog page is hosted on Blogger so server-side solution doesn’t work. Unless Blogger provides custom tags to do this, that is :). The only thing I’m allowed to do is to customize HTML of my postings.

CSS-based colorer?

I was thinking how great it would be to have a CSS-based solution so you can put your source code into HTML <SPAN> element, define a few style attributes, and let it automagically colorize everything. wow… In theory, you could build a language recognizer in JavaScript (port ANTLR), add some functions around it to convert Java-Java into HTML-Java, wrap it into DHTML behavior, and here you go. Your colorer CSS style is ready for you. The only thing is, it would take me months (or years) to cope with it :) I knew this is not an option so I started as I always start when I want to see what’s out there – Google search. I was hoping to find a colorer library with a command line interface or a Java API so I could run it or build a small program to colorize my samples offline and upload the result to my blog. This sounded much more realistic :)

Colorer on sourceforge.net

First search result brought me to the http://colorer.sourceforge.net. This was exactly what I was looking for and much more. Colorer guys created web interface where you can colorize your code fragments on the fly. This was the kicker ! Go to http://colorer.sourceforge.net/php and check it out.

Compare this:

public static String doSomething(final Integer value) {
throw new Exception("sample");
}

To this:

public static String doSomething(final Integer value) {
throw new Exception("sample");
}

Or this:

<xsl:template match="/">
<xsl:apply-templates select="node/element/@myAttr"/>
</xsl:template>

To this:

<xsl:template match="/">
<xsl:apply-templates select="node/element/@myAttr"/>
</xsl:template>


Back to CSS-based colorer

colorer-take5 solved my original problem. Now I had all my code samples colorized and nice-looking. But what about CSS-based colorer? I still wanted to make one. And now I knew how to do this :) The solution presented is really a toy that I don’t believe is useful but it works well and it seems to be very elegant.

Wow, this is an SOA and AJAX :)

I decided to use colorer web interface as a service provider. I needed to build a capability to call it, process results it returns, and wrap it into the DHTML behavior so that an HTML element can consume the service and colorize its content.



Colorer service API is just a form POST to http://colorer.sourceforge.net/php/generator.php with number of parameters. Now we need that the HTML element with some content to colorize defines new behavior that would let it consume the colorer service,

New behavior

HTML element powered by the new colorer-bridge behavior should define a few custom properties:


  • language – to tell colorer what type of language grammar to recognize

  • encoding – since colorer-take5 supports input and output encoding it’s a good idea to allow using it

  • style – to be able to use multiple color schemas supported by colorer-take5

  • mode – to define whether element should colorize itself automatically or defer this until external command to colorize received



When properties defined, the element needs to be granted with actions to actually call the colorer – colorize() - and parse the results – applyColorerResults(). Looks like we are all set to wrap it all together.

Test First

Let’s look at the HTML sample that uses the colorer-bridge component. It will be much easier to comprehend the idea by looking at how it’s used:


<html xmlns:c>
<?IMPORT namespace="c" implementation="colorer-bridge.htc"?>
<head>
<title>Colorer API Bridge Sample</title>
</head>
<body>
<!-- used as element behavior -->
<c:colorer sourceLang="jScript">
<pre>
function myFunction() {
this.property = value;
}
</pre>
</c:colorer>

<!-- another technique knows as attached behaviour -->
<pre style="behavior:url('colorer-bridge.htc')" sourceLang="xml">
<root>
<element attribute="value"/>
</root>
</pre>
</body>
</html>


As you see you can define your custom <c:colorer> element or attach new behavior to the <PRE> element. The reason we need PRE anyway is to preserve whitespaces and line breaks. Otherwise Explorer consumes everything before the component can grab it.

Colorer-Bridge Implementation

Ok. And this is our guy. The colorer-bridge.htc that does the job:


<PUBLIC:COMPONENT tagName="colorer">

<PUBLIC:PROPERTY ID="propLanguage" NAME="sourceLang"
GET="getLanguage" PUT="setLanguage"/>
<PUBLIC:PROPERTY ID="propEncodingIn" NAME="encodingIn"
GET="getEncodingIn" PUT="setEncodingIn"/>
<PUBLIC:PROPERTY ID="propEncodingOut" NAME="encodingOut"
GET="getEncodingOut" PUT="setEncodingOut"/>
<PUBLIC:PROPERTY ID="propStyle" NAME="style"
GET="getStyle" PUT="setStyle"/>
<PUBLIC:PROPERTY ID="propMode" NAME="mode"
GET="getMode" PUT="setMode"/>

<PUBLIC:ATTACH EVENT="oncontentready"
FOR="element" ONEVENT="doInit()" />

<PUBLIC:METHOD NAME="colorize" />
<PUBLIC:METHOD NAME="rollback" />

<SCRIPT>
var sWaitMessage = "Please whait while colorizing...";
var sApiUrl = "http://colorer.sourceforge.net/php/generator.php";

var sOriginalContent; // code to colorize
var oCodeSnippetContainer; // HTML element with the code
var oColorerAPI; // XmlHttpRequest object

function doInit() {
// see if applied to a PRE element
if (element.tagName == 'PRE') {
oCodeSnippetContainer = element;

// or if PRE element defined as a child
} else {
oCodeSnippetContainer =
element.getElementsByTagName("PRE")[0];
}

// remember original content to support rollback
sOriginalContent = oCodeSnippetContainer.innerHTML;
oCodeSnippetContainer.innerText = sWaitMessage;

// initialize Colorer 'API'
oColorerAPI = new ActiveXObject("Microsoft.XMLHTTP");
oColorerAPI.open("POST", sApiUrl, true);
oColorerAPI.onreadystatechange = applyColorerResponse;

if (element.mode == "auto") {
colorize();
}
}

function colorize() {
// put together POST request
var sRequest = "";
sRequest += "i_encoding=" + element.encodingIn;
sRequest += "&o_encoding=" + element.encodingOut;
sRequest += "&hrd_color=" + element.style;
sRequest += "&type=" + element.sourceLang;
sRequest += "&file_content=" + sOriginalContent;

oColorerAPI.setRequestHeader("Content-Type",
"application/x-www-form-urlencoded");
oColorerAPI.send(sRequest);
}

function applyColorerResponse() {
if (oColorerAPI.readyState == 4 && oColorerAPI.status == 200) {
// replace original code fragment with its colorized version
oCodeSnippetContainer.innerHTML = oColorerAPI.responseText;
}
}

function rollback() {
oCodeSnippetContainer.innerHTML = sOriginalContent;
}

//----------------- PROPERTIES --------------------
var sLanguage = ""; // Autodetect
var sEncodingIn = "ISO-8859-1";
var sEncodingOut = "ISO-8859-1";
var sStyle = "default"; // white
var sMode = "auto";

function getLanguage() {
return sLanguage;
}

function setLanguage(sValue) {
sLanguage = sValue;
propLanguage.fireChange();
}

// other getters and setters skipped

</SCRIPT>

<BODY>
</BODY>

</PUBLIC:COMPONENT>


That’s it. Enjoy!

P.S.

P.S. Many thanks to the Colorer team @ sourceforge.
P.P.S. Do you know the name of the guy who renamed DHTML into AJAX?