tag:blogger.com,1999:blog-35215289676750398632018-03-06T15:01:57.080-05:00ArcaneBeansBlog dedicated to my findings on developing all sort of software or helping improve open source software out there.Arcanefoamhttp://www.blogger.com/profile/15706653956555107189noreply@blogger.comBlogger5125tag:blogger.com,1999:blog-3521528967675039863.post-30031383047600590272015-10-14T09:43:00.000-05:002016-06-08T12:19:12.903-05:00DOT Debug ViewIn my past <a href="http://arcanebeans.blogspot.co.uk/2015/07/using-eclipsegraphviz-as-debugging-aid.html" target="_blank">post </a>I presented a small hack that used the returned value of the toDOT() method of a variable in the debug view (in Eclipse) to generate a graph in the <a href="https://github.com/abstratt/eclipsegraphviz" target="_blank">eclipsegraphviz</a>&nbsp; view. Today I am presenting the new, improved, version of this.<br /><br /><h3>No more hacking into the Eclipsegraphviz view</h3><div>After discussing my idea with Rafael Chaves (<a href="https://github.com/abstratt" target="_blank">@abstratt</a>), the author of&nbsp;Eclipsegraphviz, he was kind enough to provide a nicer method to access the view provided by his plugin. Thanks a lot Rafael!</div><div><br /></div><h3>The DOT Debug View plugin</h3><div>My initial hack didn't play nicely with with the thread management principles of both the Elcipse UI and the JDT. Hopefully this has been corrected, and thus using the new plugin should not had adverse effects on the debugger or the UI.</div><div><br /></div><div><h3>Providing an Eclipse Update Site</h3></div><div><br /></div><div>When developing plugins I always had the issue of distribution and subsequent updates. Eclipse provides a very solid update framework through via its p2 component. However, if you don't own a domain or hosting space, making your updatesite available might be tricky.&nbsp;</div><div><br /></div><div>Luckily, I fumbled upon&nbsp;<a href="http://bintray.com/" target="_blank">bintray</a>&nbsp;while looking for alternatives. Based on the script provided by Lars Vogel, you can read this <a href="http://blog.vogella.com/2014/12/03/publish-an-eclipse-p2-update-site-at-bintray-via-a-shell-script/" target="_blank">post</a> about it, I came up with a Python version to help me (and hopefully others) publish their update sites to bintray. You can find the script here: <a href="https://github.com/arcanefoam/py_bintray-publish-p2-updatesite" target="_blank">py_bintray-publish-p2-updatesite</a>.&nbsp;</div><div><br /></div><div><h3>Using DOT Debug View plugin</h3></div><div>As before, all you need to do is provide a method with the signature "String toDOT()" in your class, and return a valid DOT graph string of a representation/view of your object. Of course you need to install Eclipsegraphviz too!.</div><div><br /></div><div>You can find more details on the project's gitbub <a href="https://github.com/arcanefoam/dotdebugview" target="_blank">page</a>, but basically you can add the following url to your update sites and install it:</div><div><br /></div><div>https://dl.bintray.com/arcanefoam/DOTDebugView</div><div><br /></div><div><br /></div><div><br /></div>Arcanefoamhttp://www.blogger.com/profile/15706653956555107189noreply@blogger.com0tag:blogger.com,1999:blog-3521528967675039863.post-13880738871980302692015-07-13T09:00:00.000-05:002015-07-14T02:48:54.049-05:00Using EclipseGraphviz as a debugging aidMy current area of interest is Model Driven Architecture. In this field, and I assume in many others too, our [data] models can be usually represented as graphs. In this scenarios, being able to view our model as an actual graph can be of great help to quickly identify errors in our model.<br /><br /><h3>Come in&nbsp;EclipseGraphviz</h3><a href="http://www.graphviz.org/" target="_blank">Graphviz </a>is a Graph Visualization Software that can produce graph visualizations from a graph representation in a text file (using the <a href="http://www.graphviz.org/content/dot-language" target="_blank">DOT language</a>).&nbsp;<a href="https://github.com/abstratt/eclipsegraphviz" target="_blank">EclipseGraphviz </a>is a plugin for Eclipse that adds a view in which files with the dot extension will be rendered using Graphviz dot tool.<br /><br />However, having the need to have a dot file requires you to somehow serialize your model into a file. This limits the use of the viewer for debugging purposes. Surely you can use the console to output a graph in DOT language, then copy the text, paste it to a file... or modify your code to serialize directly into a file...<br /><br />Wouldn't it be nice to click on a variable in the <b>Variable View</b>&nbsp;during a debug session and have the graph representation of it shown in a nice graph?<br /><br /><h3>EclipseGraphviz + debug</h3>I have extended&nbsp;EclipseGraphviz to make this happen. All you have to do is add a <br /><pre class="brush:java" style="text-align: center;">String toDOT();</pre>method to any class. If the returned string is a valid DOT graph, then you will see it in the View. Simple as that...<br /><br />This lets you rapidly instrument classes to have a graph representation during debug and doesn't make your projects depend on any of the EclipseGraphviz plugins. It also allows you to keep the toString() method independent if you have a string representation that might be used for other purposes. Having a toDOT() method will also provide you with a means to serialize your model if it needs be.<br /><br />Bellow you can see a snapshot of how it works. I have selected variable p in the Variables view. The ExecutionPlan class has a toDOT() method. In this case the toString() method just calls toDOT() so you can see the DOT representation of the class in the Variables view.<br /><br /><div class="separator" style="clear: both; text-align: left;"><a href="http://1.bp.blogspot.com/-EU2UgbPePM4/VaPBUillA9I/AAAAAAAAAGk/EANH54WhXhs/s1600/Capture.PNG" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img border="0" height="640" src="http://1.bp.blogspot.com/-EU2UgbPePM4/VaPBUillA9I/AAAAAAAAAGk/EANH54WhXhs/s640/Capture.PNG" width="520" /></a></div><div class="separator" style="clear: both; text-align: center;"><br /></div><div class="separator" style="clear: both; text-align: left;"><br /></div><h3 style="clear: both; text-align: left;">Installing</h3><div>The source code is available from&nbsp;<a href="https://github.com/arcanefoam/eclipsegraphviz" target="_blank">GitHub</a> (EclipseGraphviz &nbsp;fork). I think the easiest way is to follow EclipseGraphviz &nbsp;installation instructions and then build and export my version to your <i>dropins</i> folder in Eclipse.<br /><br /><br /></div>Arcanefoamhttp://www.blogger.com/profile/15706653956555107189noreply@blogger.com2tag:blogger.com,1999:blog-3521528967675039863.post-70749281441580334482013-11-30T09:42:00.001-05:002013-11-30T09:49:27.002-05:00Extended EMC for CVS Files<h3 style="text-align: center;"><em>An extended Epsilon Model Connectivity (EMC) layer for working with Comma-separated Values (CSV)&nbsp;files/models.</em></h3><em><span style="font-size: small;">Note to the reader: Some of my instructions assume you have worked with the Epsilon languages and tools before.</span></em><br /><strong></strong><br /><a href="http://www.eclipse.org/epsilon/">Epsilon</a>&nbsp;family of languages ad set of tools provide a lot of flexibility when doing al sort of stuff with models. One of the things I like the most is the ability to work with models from domains other than EMF. Epsilon provide support for working with BibTex, CSV, and other type of files/models. Find all the info <a href="http://www.eclipse.org/epsilon/doc/emc/">here</a>. However, some of the drivers only provide basic support. This is the case of the CSV (comma-separated values), which currently only supports loading the CSV model (file) and accessing each of its rows as an element in the model. So basically you where limited to something in the lines of&nbsp; the following EOL script:<br /><strong><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"></span></span></strong><br /><div align="LEFT"><strong><span style="font-size: small;"><span style="color: #7f0055;"><span style="color: #7f0055;"><b>for</b></span></span> (r <b><span style="color: #7f0055;"><span style="color: #7f0055;">in</span></span></b> csv!Row.all()) {</span></strong></div><strong><span style="font-size: small;"> <b><span style="color: #7f0055;"><span style="color: #7f0055;">&nbsp;&nbsp;&nbsp; if</span></span></b> (r.at(2)) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r.at(0).print();</span></strong><br /><span style="font-size: small;"><strong>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; r.at(1).print();<br /><span style="color: #2a00ff;"><span style="color: #2a00ff;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ", is invited to the party!"</span></span>;</strong> </span><br /><div align="LEFT"><span style="font-size: small;">&nbsp;&nbsp;&nbsp; }</span></div><span style="font-size: small;">}</span><br /><strong></strong><br />Which assumes you have a CSV file of the form "name,lastName,isInvited", and you want to print the list of invited guests. Although your EOL script could be more complex you are still limited to read-only operations and index access of fields. <br /><strong></strong><br />I am currently working on an extended version of the Epsilon's CSV EMC to provide additional capabilities. Essentially I want to&nbsp;provide support to&nbsp;access fields in each row by using the header information as navigation and that also supports modifying the model. You can find the source code in the <a href="https://code.google.com/p/epsilonlabs/">Epsilon Labs</a> (at Google code). You will need to check out two projects (if you have never worked&nbsp;with SVN repositories&nbsp;in eclipse this&nbsp;link provides all the info you need. Look under <a href="http://www.eclipse.org/subversive/installation-instructions.php">installation instructions</a>&nbsp;and how to <a href="http://www.eclipse.org/subversive/documentation/teamSupport/find_check_wiz.php">check out a repository</a>.)&nbsp;: <span style="font-family: &quot;Courier New&quot;, Courier, monospace;">org.eclipse.epsilon.emc.csv</span> and <span style="font-family: &quot;Courier New&quot;, Courier, monospace;">org.eclipse.epsilon.emc.csv.dt</span><span style="font-family: inherit;">, under <a href="http://epsilonlabs.googlecode.com/svn/trunk/ImprovedCSVHHR">svnRepo</a>. I will recommend importing this projects to your Eclipse Workspace and then running a nested eclipse to try them out. Replacing your default plugins by this ones might work, but I haven't tested that option. You can also checkout the <span style="font-family: &quot;Courier New&quot;, Courier, monospace;">org.eclipse.epsilon.emc.csv.test</span> project, which has the examples I will be using onwards.</span><br /><h4>Loading a CSV model</h4>Once the plugins are in place you will be able to use the extended CSV model loader with new options. If you don't see the csv model when adding a model to the lunch configuration be sure to click the "Show all model types" check box.<br /><br /><div class="separator" style="clear: both; text-align: center;"><a href="http://4.bp.blogspot.com/-3z-peEz59b4/UpjZ2cVI-II/AAAAAAAAAFk/b4xReDpDLRw/s1600/CSVModelLoader.PNG" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><strong><img border="0" src="http://4.bp.blogspot.com/-3z-peEz59b4/UpjZ2cVI-II/AAAAAAAAAFk/b4xReDpDLRw/s1600/CSVModelLoader.PNG" height="318" width="320" /></strong></a></div><br />If you have worked with CSV models before, you will notice that now have a Load/Save section and a new CSV section to set some of the attributes of your CSV model.<br /><strong></strong><br /><ul><li>The <strong>Field Separator</strong> allows you to select a different separator than comma.... yes, they are called comma-separated files, but sometimes a colon, or a semi-colon, or other char is used as a field separator. Now you can tell the model loader which one too use. By default it is a comma.</li><li>The <strong>Known Headers</strong> tells the loader that the first row of your file contains headers. Headers can late be used to access fields of a row. </li><li>The <strong>Varargs Header</strong> tells the loader that although the first row has headers, some of the rows in the file may not have values for all of them. I know this is not the "standard" (did you know that <a href="http://tools.ietf.org/html/rfc4180">RFC 4180</a> describes CSV file standards?), but my particular&nbsp;CSV files did so I took the liberty to add it.</li></ul><br /><ul></ul><h4>Lets see how the header information works now inside&nbsp;EOL scripts.</h4>Lets assume I have a CSV model with the following information (some baseball statistics, but I am not a baseball fan):<br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">Rk,Year,Age,Tm,Lg,,W,L,W-L%,G,Finish</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">1,1978,37,Atlanta Braves,NL,,69,93,.426,162,6</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">2,1979,38,Atlanta Braves,NL,,66,94,.413,160,6</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">3,1980,39,Atlanta Braves,NL,,81,80,.503,161,4</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">4,1981,40,Atlanta Braves,NL,,25,29,.463,55,4</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">5,1981,40,Atlanta Braves,NL,,25,27,.481,52,5</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">6,1982,41,Toronto Maple Leafs,AL,,78,84,.481,162,6</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">7,1983,42,Toronto Maple Leafs,AL,,89,73,.549,162,4</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">8,1984,43,Toronto Maple Leafs,AL,,89,73,.549,163,2</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif;">9,1985,44,Toronto Maple Leafs,AL,,99,62,.615,161,1</span><br /><br />If you specify the Known Headers option, then each record (row) of your model will have the attributes Rk, Year, Age, Tm, Lg, etc.. Hence, you can do stuff like this (assuming you named your model baseball):<br /><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"></span></span><br /><div align="LEFT"><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"><b>for</b></span></span><span style="font-size: small;"> (row </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">in</span></span></b><span style="font-size: small;"> baseball!Row.all()) {</span></div><div align="LEFT">&nbsp;&nbsp;&nbsp; row.Tm.println();</div>}<br /><br />I know, its is simple, but enough to show you that now u can access fields in a row by the header name. Pretty neat ah? (You can find this in TestFieldRead.eol)<br /><br /><h4>Saving model modifications: </h4>If you set the "Store on Disposal" value to true, a script like this will change the team name every time u run it. (TestFieldWrite.eol)<br /><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"></span></span><br /><div align="LEFT"><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"><b>for</b></span></span><span style="font-size: small;"> (row </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">in</span></span></b><span style="font-size: small;"> baseball!Row.all()) {</span></div>&nbsp;&nbsp;&nbsp; row.Tm.println(); <br /><div align="LEFT"><span style="font-size: small;"> </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">&nbsp;&nbsp;&nbsp; if</span></span></b><span style="font-size: small;"> (row.Tm == </span><span style="color: #2a00ff; font-size: small;"><span style="color: #2a00ff; font-size: small;">"Toronto Blue Jays"</span></span><span style="font-size: small;">) {</span></div><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: small;">row.Tm = </span><span style="color: #2a00ff; font-size: small;"><span style="color: #2a00ff; font-size: small;">"Toronto Maple Leafs"</span></span><span style="font-size: small;">;</span><br /><div align="LEFT"><span style="font-size: small;"> &nbsp;&nbsp;&nbsp; } </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">else</span></span></b><span style="font-size: small;"> </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">if</span></span></b><span style="font-size: small;"> (row.Tm == </span><span style="color: #2a00ff; font-size: small;"><span style="color: #2a00ff; font-size: small;">"Toronto Maple Leafs"</span></span><span style="font-size: small;">) {</span></div><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: small;">row.Tm = </span><span style="color: #2a00ff; font-size: small;"><span style="color: #2a00ff; font-size: small;">"Toronto Blue Jays"</span></span><span style="font-size: small;">;</span><br /><span style="font-size: small;">&nbsp;&nbsp;&nbsp; </span><span style="font-size: small;">}</span><br /><div align="LEFT"><span style="font-size: small;">}</span></div><span style="font-size: small;"></span><br /><h4>Varargs Headers</h4>The concept of <em>varargs</em> is used in Java to define methods that accept a variable number of parameter (variable arguments). This can happen in a CSV file, where maybe there is a known set of headers that all records have, but also each record may have additional fields beyond this headers. Lets me show this with an example. I do my reference management with <a href="http://www.qiqqa.com/54808">Qiqqa</a>, which among other things, lets me export a CSV file of my reference matrix. It looks like this (after some editing to remove the comments and leave only two headers):<br /><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif; font-size: small;">source,target<br />Corradini.etal1996,<br />Agrawal.etal2006,<br />DiRuscio.etal2012,Stevens2007,Czarnecki.etal2009,Czarnecki.Helsen2006,Stevens2008,Stevens2010,Selic2003,<br />Syriani.Vangheluwe2010,Agrawal.etal2006,Jouault.Kurtev2006a,<br />Aranda.etal2012,France.Rumpe2007,Schmidt2006,Kent2002,<br />France.Rumpe2007,Czarnecki.Helsen2006,MDA1.0.1,<br />Schmidt2006,<br />Kent2002,<br />Atkinson.Kuhne2003,<br />Baudry.etal2006,Fleurey.etal2004,<br />Vallecillo.etal2012,DiRuscio.etal2012,France.Rumpe2007,Czarnecki.Helsen2006,Stevens2008,Baudry.etal2006,</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif; font-size: small;">Patzina.Patzina2012,Kolovos.etal2008,Wimmer.etal2011,Jouault.Kurtev2006a,Taentzer.etal2005,Czarnecki.Helsen2006,</span><br /><span style="font-family: Times, &quot;Times New Roman&quot;, serif; font-size: small;">Baudry.etal2010,Schmidt2006,France.Rumpe2007,<br />Guerra2012,Kolovos.etal2008,Baudry.etal2010,<br />Bezivin2004,OCL2.3.1,Bezivin.Gerbe2001,<br />Favre2004,MDA1.0.1,Bezivin.Gerbe2001,Bezivin2004,Kent2002,</span><br /><br />In such a file, there is always a source filed, but there may be zero or more targets. If you set the <strong>Varargs Headers</strong> option to true (check it), the CSV model loader will create a record that has a source and a target attribute (or as many fields in your header), and the last header will be a collection. This collection will hold all the fields in each row after and including the last header. IN the previous example, target is the last header, so the header attribute is a collection. <br /><br />So now, if u want to print a list of all targets of each source, your script may look like this(TestFieldRead.eol) :<br /><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"></span></span><br /><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"></span></span><br /><div align="LEFT"><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;"><b>for</b></span></span><span style="font-size: small;"> (row </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">in</span></span></b><span style="font-size: small;"> qiqqa!Row.all()) {</span></div><span style="font-size: small;">&nbsp;&nbsp;&nbsp; </span><span style="font-size: small;">row.source.print();</span><br /><span style="font-size: small;"></span><span style="color: #2a00ff; font-size: small;"><span style="color: #2a00ff; font-size: small;">&nbsp;&nbsp;&nbsp; " is connected to "</span></span><span style="font-size: small;">.println();</span><br /><span style="font-size: small;">&nbsp;&nbsp;&nbsp; </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">for</span></span></b><span style="font-size: small;"> (t </span><b><span style="color: #7f0055; font-size: small;"><span style="color: #7f0055; font-size: small;">in</span></span></b><span style="font-size: small;"> row.target) {</span><br /><span style="font-size: small;">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </span><span style="font-size: small;">t.println();</span><br /><span style="font-size: small;"> &nbsp;&nbsp;&nbsp; } <br />}</span><br /><h4>Header-less CSV files</h4>Finally, for support of header-less CSV files and to make it easy to use your existing scripts, if you don´t check the <strong>Known Headers</strong> option, the CSV model loader ill create a record for each row and a default attribute <em>field</em>. This attribute is collection which holds all the fields in each row.<br /><br /><h4>Final Notes</h4>Please use the Epsilon <a href="http://www.eclipse.org/forums/index.php/f/22/">forum</a> for any technical questions or discussions :D. <br />Arcanefoamhttp://www.blogger.com/profile/15706653956555107189noreply@blogger.com0tag:blogger.com,1999:blog-3521528967675039863.post-11152395467568118772010-08-16T16:21:00.013-05:002010-08-16T19:02:51.605-05:00Project Type Module + ChildFactoryAfter completing the Project Module tutorial (found <a href="http://platform.netbeans.org/tutorials/nbm-projecttype.html">here</a>), I got a nice folder structure... However version control folders and other files that where not relevant to the project where displayed under the project structure. I wanted to display only the relevant files and folders and hide the rest. Next I show how to build a ChildFactory that does exactly what I needed.<br /><br />In the original tutorial at Step 2 of section <span style="font-weight: bold;">Creating the Logical View Provider</span>, you defined a TextNode that has the following constructor:<br /><pre class="brush:java"><br />public TextNode(Node node, DemoProject project) throws DataObjectNotFoundException {<br />super(node, new FilterNode.Children(node),<br />//The projects system wants the project in the Node's lookup.<br />//NewAction and friends want the original Node's lookup.<br />//Make a merge of both<br />new ProxyLookup(new Lookup[]{Lookups.singleton(project),<br />node.getLookup()<br />}));<br />this.project = project;<br />}</pre><br />The important bit to notice is the <span style="font-style: italic;">new FilterNode.Children(node)</span> part. This is basically creating a default Children layout for your project. By default I mean that all child files and folders under your main project folder will be displayed under the project node tree. In my specific case the tree I got can be seen in the following figure, where for example you can see version control folders (.svn).<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_l8cMUA5_HaQ/TGm937TkokI/AAAAAAAAABA/dwNgpfcig9U/s1600/FullChildTree.png"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 187px; height: 320px;" src="http://3.bp.blogspot.com/_l8cMUA5_HaQ/TGm937TkokI/AAAAAAAAABA/dwNgpfcig9U/s320/FullChildTree.png" alt="" id="BLOGGER_PHOTO_ID_5506140787947381314" border="0" /></a><br /><br /><br />To change this behavior we need to create a new custom child factory. First create a class that extends org.openide.nodes.ChildFactory&lt;T>:<br /><pre class="brush:java"><br />public class AddonChlidFactory extends org.openide.nodes.ChildFactory&lt;String> {<br /><br /> FileObject folder;<br /><br /> public AddonChlidFactory(FileObject folder) {<br /> this.folder = folder;<br /> }<br />}</pre><br /><br />The <span style="font-weight: bold;">folder FileObject</span> will be used later for inspecting subfolders and filtering them as needed. The ChildFactory works by creating a node for each key in a set of keys (the set of keys is arbitrary). Basically, you supply the set of keys and then the IDE asks you to give him the node(s) that should be generated according to the key. According to the API documentation the set of keys should be generated dynamically (probably according to the actual files and folders in your project folder). But for simplicity I created it statically. My set contains the extensions of the files I am interested and the <span style="font-style: italic;">folders</span> string to provide nodes for folders. This is how my createKeys method looks:<br /><pre class="brush:java"><br />@Override<br /> protected boolean createKeys(List&lt;String> list) {<br /> // File types of interest<br /> list.add("lua");<br /> list.add("toc");<br /> list.add("xml");<br /> list.add("ttf");<br /> list.add("tga");<br /> list.add("blp");<br /> list.add("txt");<br /> list.add("folders");<br /> return true;<br /> }<br /></pre><br />Next, we need to decide what nodes to return for each key. For data files we will just return the node representing the file. For folders we will inspect the folder name and only return nodes if some conditions are met. Specifically we are interested in NOT returning nodes for version control folders. Next is the code of the <span style="font-weight: bold;">createNodesForKey</span> method:<br /><pre class="brush:java"><br /> @Override<br /> protected Node[] createNodesForKey(String key) {<br /> ArrayList&lt;Node> nodes = new ArrayList&lt;Node>();<br /> if(key.equals("folders")) {<br /> /* add folder nodes */<br /> for(FileObject o : Collections.list(folder.getFolders(false))) {<br /> if(o.getName().equals("nbproject")) {<br /> try {<br /> nodes.add(new nbProjectNode(o));<br /> } catch (DataObjectNotFoundException ex) {<br /> Exceptions.printStackTrace(ex);<br /> }<br /> } else if(!(o.getName().equals(".svn")<br /> || o.getName().equals(".cvs")<br /> || o.getName().equals(".hg"))) {<br /> // Add all folders that are not version control<br /> Node addonFolderNode = DataFolder.findFolder(o).getNodeDelegate();<br /> nodes.add(new AddonFolderNode(addonFolderNode, o));<br /> }<br /> }<br /> } else {<br /> // Add data files<br /> for(FileObject o : Collections.list(folder.getData(false))) {<br /> if (o.hasExt(key)) {<br /> try {<br /> Node addonNode = DataObject.find(o).getNodeDelegate();<br /> nodes.add(new FilterNode(addonNode, Children.LEAF));<br /> } catch (DataObjectNotFoundException ex) {<br /> Exceptions.printStackTrace(ex);<br /> }<br /> }<br /> }<br /> }<br /> return(nodes.toArray(new Node[nodes.size()]));<br /> }<br /></pre><br />You can notice that for the folders keyword I have two options: First, if the folder is the nbproject folder we create a nbProjectNode (based on the <a href="http://platform.netbeans.org/tutorials/nbm-projectextension.html">NetBeans Project Type Extension Module Tutorial</a>). Second, if the folder name is not any of the common revision control folder names a node is returned. For data files, we create nodes for any files who's extension is one of the keys. Notice also that for folders we used our own AddonFolderNode node, for which we have a nested class. This will allow us to define custom behavior for our inner project folders (for example what actions can be invoked). Putting it all together we will have:<br /><pre class="brush:java"><br />public class AddonChlidFactory extends org.openide.nodes.ChildFactory&lt;String> {<br /><br /> FileObject folder;<br /><br /> public AddonChlidFactory(FileObject folder) {<br /> this.folder = folder;<br /> }<br /><br /> @Override<br /> protected boolean createKeys(List&lt;string> list) {<br /> // File types of interest<br /> list.add("lua");<br /> list.add("toc");<br /> list.add("xml");<br /> list.add("ttf");<br /> list.add("tga");<br /> list.add("blp");<br /> list.add("txt");<br /> list.add("folders");<br /> return true;<br /> }<br /><br /> @Override<br /> protected Node[] createNodesForKey(String key) {<br /> ArrayList&lt;Node> nodes = new ArrayList&lt;Node>();<br /> if(key.equals("folders")) {<br /> /* add folder nodes */<br /> for(FileObject o : Collections.list(folder.getFolders(false))) {<br /> if(o.getName().equals("nbproject")) {<br /> try {<br /> nodes.add(new nbProjectNode(o));<br /> } catch (DataObjectNotFoundException ex) {<br /> Exceptions.printStackTrace(ex);<br /> }<br /> } else if(!(o.getName().equals(".svn")<br /> || o.getName().equals(".cvs")<br /> || o.getName().equals(".hg"))) {<br /> // Add all folders that are not version control<br /> Node addonFolderNode = DataFolder.findFolder(o).getNodeDelegate();<br /> nodes.add(new AddonFolderNode(addonFolderNode, o));<br /> }<br /> }<br /> } else {<br /> // Add data files<br /> for(FileObject o : Collections.list(folder.getData(false))) {<br /> if (o.hasExt(key)) {<br /> try {<br /> Node addonNode = DataObject.find(o).getNodeDelegate();<br /> nodes.add(new FilterNode(addonNode, Children.LEAF));<br /> } catch (DataObjectNotFoundException ex) {<br /> Exceptions.printStackTrace(ex);<br /> }<br /> }<br /> }<br /> }<br /> return(nodes.toArray(new Node[nodes.size()]));<br /> }<br /><br /><br /> private static final class AddonFolderNode extends FilterNode {<br /><br /> FileObject folder;<br /><br /> public AddonFolderNode(Node node, FileObject folder) {<br /> super(node, Children.create(new AddonChlidFactory(folder), true));<br /> this.folder = folder;<br /> }<br /><br /> @Override<br /> public Action[] getActions(boolean arg0) {<br /> Action[] nodeActions = new Action[1];<br /> nodeActions[0] = CommonProjectActions.newFileAction();<br /> return nodeActions;<br /> }<br /><br /> @Override<br /> public String getDisplayName() {<br /> return folder.getName();<br /> }<br /><br /> }<br /><br /> private static final class nbProjectNode extends FilterNode {<br /><br /> public nbProjectNode(FileObject node) throws DataObjectNotFoundException {<br /> super(DataFolder.findFolder(node).getNodeDelegate());<br /> }<br /><br /> @Override<br /> public String getDisplayName() {<br /> return "Important Files";<br /> }<br /> }<br /><br />}</pre><br /><br />Finally on our project view we get (look that the svn folder is gone, the nbproject fodler is renamed to Important Files and how files are sorted by type):<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_l8cMUA5_HaQ/TGnP9cT3LcI/AAAAAAAAABI/o_4lUh6ca4Y/s1600/FinalChildTree.png"><img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 199px; height: 320px;" src="http://4.bp.blogspot.com/_l8cMUA5_HaQ/TGnP9cT3LcI/AAAAAAAAABI/o_4lUh6ca4Y/s320/FinalChildTree.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5506160673915612610" /></a>Arcanefoamhttp://www.blogger.com/profile/15706653956555107189noreply@blogger.com0tag:blogger.com,1999:blog-3521528967675039863.post-15267290619059293992010-07-29T15:29:00.016-05:002010-08-17T10:00:46.398-05:00Project Type Module + Privileged TemplatesI started working on a Project Module for managing WoW addons (<a href="http://luatopping.sourceforge.net/">Lua Topping</a>). After fidling arround I got the nodes to work as intended (posted <a href="http://arcanebeans.blogspot.com/2010/08/project-type-module-childfactory.html">here</a>) I wanted to have a nice set of templates for the basic addon files: lua, toc and xml, plus some specific to <a href="http://www.wowace.com/">WowAce</a>. You can look at the NetBeans tutorial <a href="http://platform.netbeans.org/tutorials/nbm-projecttype.html">here</a>.<br /><br />Taking a look at the NetBeans API and the <a href="http://bits.netbeans.org/6.8/javadoc/org-netbeans-modules-projectapi/org/netbeans/api/project/Project.html">Project</a> class I found that for the lookup you could add a PrivilegedTemplates interface. This interface only has one method:<br /><br /><pre class="brush:java">String[] getPrivilegedTemplates()</pre><br /><br />According to the API documentation the returned string array should contain "full paths to privileged templates, e.g. Templates/Other/XmlFile.xml". But what is the correct <span style="font-weight: bold;">full path</span> for a template?. The simplest way to find out is to view your <span style="font-style: italic;">layer.xml</span> file (you should read about NetBeans and layer files if you want details on how they work). In my case, the Templates section of my layer.xml file looks like this:<br /><br /><pre class="brush:xml"><br />&lt;folder name="Templates"><br /> &lt;folder name="WoW"><br /> &lt;file name="TocTemplate.toc" url="TocTemplate.toc"><br /> &lt;attr name="displayName" bundlevalue="net.sourceforge.luatopping.wow.addon.project.Bundle#Templates/WoW/TocTemplate.toc"/><br /> &lt;attr name="template" boolvalue="true"/><br /> &lt;/file><br /> &lt;file name="WoWUIxmlTemplate.xml" url="WoWUIxmlTemplate.xml"><br /> &lt;attr name="displayName" bundlevalue="net.sourceforge.luatopping.wow.addon.project.Bundle#Templates/WoW/WoWUIxmlTemplate.xml"/><br /> &lt;attr name="template" boolvalue="true"/><br /> &lt;/file><br /> &lt;file name="Template.pkgmeta" url="Template.pkgmeta"><br /> &lt;attr name="displayName" bundlevalue="net.sourceforge.luatopping.wow.addon.project.Bundle#Templates/WoW/Template.pkgmeta"/><br /> &lt;attr name="template" boolvalue="true"/><br /> &lt;/file><br /> &lt;/folder><br />&lt;/folder></pre><br /><br />It turns out the correct <span style="font-weight: bold;">full path</span> for a template is the path starting at Templates/ and finishing at the template name (as in the name property of the file entry), containing all the nested sub folders. So in my case, for the TocTemplate.toc template this would be: Templates/WoW/TocTemplate.toc.<br /><br />Putting it all together (assuming you have followed the NetBeans tutorial):<br /><ol><li>Add a PrivilegedTemplates implementation to your project lookup<br /><pre class="brush:java"><br />//The project type's capabilities are registered in the project's lookup:<br />@Override<br />public Lookup getLookup() {<br /> if (lkp == null) {<br /> lkp = Lookups.fixed(new Object[]{<br /> state, //allow outside code to mark the project as needing saving<br /> new ActionProviderImpl(), //Provides standard actions like Build and Clean <br /> new WowAddonDeleteOperation(),<br /> new WowAddonCopyOperation(this),<br /> new Info(), //Project information implementation<br /> new WowAddonProjectLogicalView(this), //Logical view of project implementation<br /> new WowAddonProjectCustomizer(this), //Customizer for the project<br /> new WowAddonProjectPrivilegedTemplates(),<br /> });<br /> }<br /> return lkp;<br />}</pre><br /><br /></li><li>In you PrivilegedTemplates implementation construct the desired templates names array<br /><pre class="brush:java"><br />private final class WowAddonProjectPrivilegedTemplates implements PrivilegedTemplates {<br /><br /> private String[] privileged = new String[]{<br /> "Templates/WoW/TocTemplate.toc",<br /> "Templates/Other/LuaTemplate.lua",<br /> "Templates/WoW/WoWUIxmlTemplate.xml",<br /> "Templates/WoW/Template.pkgmeta",<br /> };<br /><br /> public String[] getPrivilegedTemplates() {<br /> return privileged;<br /> }<br />}</pre><br /></li></ol><br /><br />That's it!. When you run your Project Module, your templates should be visible when you right click the project node and select the <span style="font-weight: bold;">New </span>option:<br /><br /><a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_l8cMUA5_HaQ/TFIIF7k3kGI/AAAAAAAAAA4/HdMALWoQrTk/s1600/templatesmenu.jpg"><img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 320px; height: 253px;" src="http://3.bp.blogspot.com/_l8cMUA5_HaQ/TFIIF7k3kGI/AAAAAAAAAA4/HdMALWoQrTk/s320/templatesmenu.jpg" alt="" id="BLOGGER_PHOTO_ID_5499466992957493346" border="0" /></a>Arcanefoamhttp://www.blogger.com/profile/15706653956555107189noreply@blogger.com0