Let’s solve the Recursion

The code posted below is a very simple recursive code for performing the search operation. Reading the code will force you to follow a visually cyclic path but instead visualising the search operation as a traversal along the tree from the root node to the leaf node of the last top level node follows a straight visual path.

Most of the single recursive methods can be split into group of atomic methods .

//iterate the collection (this is where the recursion is imposed)

//validate the input object against the object of interest

——————————————————————–

//Finds the first treeNode encountered in the Hunt operation, which is tagged to the //specified object.

//Returns Null, if the search operation fails to find a node tagged to the object specified Or if the specified object/nodeCollection is Null.
private TreeNode FindNode(object taggedObject, TreeNodeCollection nodes)
{
//validate inputs
if (taggedObject == null || nodes == null)
{ return null; }

TreeNode nodeToReturn = null;
foreach (TreeNode node in nodes)
{
if (Equals(node.Tag, taggedObject))
{
return node;
}

//Now search in children of this node.
nodeToReturn = FindNode(taggedObject, node.Nodes);
if (nodeToReturn != null)
{
return nodeToReturn;
}
}
//no matching node found. Hence return null!!
return null;
}

Advertisements

How do you dish out the current obect in the context?

The environment may dish out all the possible operations that could be performed on the current object in the context to the user from different sources. It could be from the right click context menu on the current object or the cute icon in the toolbar or the menuItem in the global menu bar or a tree layout in the side bar or an icon in the tray.

The same set of operations may be offered by multiple sources. Then
Q.How can we manage to keep all of them in sync?

>The context specific menu items must be populated dynamically. They must never be cached with the current object

>All the menuItems/icons must be tagged to their associated actions , i.e, if a certain operation is disabled for the given context then all the UI components offering that particular operation must be disabled or should not be populated at all.

To achieve this, possibly you must have a registration mechanism of all the operations against the UI components offering them.

>Populate the ContextMenu with the selection oriented commands

>Populate the ContextMenu with a fixed set of comands for each selection type and then enable/disable them to reflect the selection state.

This will help the user to see an expected and same set of consistent context menu and will make navigation easier.

>The context specific menu must be consistent across all the object displayed in the editor.

>If the same object appears in more than one editor, then same contextMenu must be available.

>Classify and group the menuItems using menu separators.

>Provide the common operations on the editor itself, which are not selection specific rather than on each of the object. For eg: Reload, SelectAll, Back, Undo/Redo, Find, Zoom

Q. How can the editor cope up when the menuItems, which might possibly be contributed by a add-ons and plugins?

>There must be a registration mechanism to allow the menuItems dispayed in the editor to be registered with the HostControl.

>Implement a command filter for each object type in the editor.

Some dumb questions to ask…

>Whats the best way to provide response to the functionality from the editor but inturn being offered by different objects?

>If the functionality of an operation is provided by a specific object in the editor, then that particular functionality must be exposed as public method inorder to allow the editor to provide response to the menuItemClick.

>If for the sake of consistency, all the context specific menuItems are being populated by the editor, then the editor must be able to exactly locate and identify the current Object and must enable/disable menuItems accordingly but how to do it cleanly?

>If the effect of an operation being performed by using object1 or object2 is same, then why have functionality available from both objects?

Writing about it so far has helped me in clearing some blockages and probably would be in a better state to comment more on it once I solve it completely.

Coding vs Programming

Coding is a mere typing exercise to express the logical sequence of steps in the form of a well indented paragraph. It is specific to syntax and the language.

Programming is problem solving exercise, which forces you think continuously. Its a two step process.

First and the most crucial step is to “Define the Problem Statement”. This is the most important and difficult step as all the use cases which needs to be satisfied must be clearly identified. The accuracy of the solution will depend on this step. This doesn’t depend on how you express. It could be a flow chart or diagrams depicting the problem statement.

Second step is to provide a logical sequence of the steps. The elegance, re-usability and scalability of the solution will depend on this step, as domain/technology specific knowledge is essential.

After some experience in using the msdn, I am confident that any software technology can be easily learnt by spending some time with a well documented API.

Hence a solid first step, is essential for a rock solid solution.

Count your LOC!!

How many Lines of Code(LOC), have you produced? For those wondering, how many lines of code have they generated, here is a sample code in C#.

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace LOC
{
class Loc
{
private const string fileExtension = ".cs";

private static void Main(string[] args)
{
string path = "";
if (args.Length == 0)
{
//PrintUsageAndExit();
Console.WriteLine("Enter the path of the root folder containing the source code" + Environment.NewLine);
path = Console.ReadLine();
}
else
{
path = args[0];
}

if (string.IsNullOrEmpty(path) || !Directory.Exists(path))
{
PrintUsageAndExit();
}

DirectoryInfo parent = new DirectoryInfo(path);

int lines = CountLines(parent);
Console.WriteLine("The Total Number of Lines is :" + lines);
Console.ReadLine();
}

private static void PrintUsageAndExit()
{
Console.WriteLine("Expecting the path of a Directory containing Files or Sub Folders with file extension as " + fileExtension);

Console.WriteLine(Environment.NewLine + "Press any key to exit...");
Console.ReadLine();
Environment.Exit(1);
}

private static int CountLines(DirectoryInfo dir)
{
int lines = 0;
//process all the files in the directory
foreach (FileInfo f in dir.GetFiles())
{
//process only the files with the required file extension
if (f.Name.EndsWith(fileExtension))
{
lines += CountLines(f);
}

}

//process all the sub directories in the current directory
DirectoryInfo[] children = dir.GetDirectories();
foreach (DirectoryInfo d in children)
{
lines += CountLines(d);
}
return lines;
}

private static int CountLines(FileInfo file)
{
StreamReader reader = null;
int lines = 0;
try
{
reader = new StreamReader(file.FullName);

while (!reader.EndOfStream)
{
string line = reader.ReadLine().Trim();

//ignore blank lines and comments...TODO: Also ignore commented code with in /* ... */
if (string.IsNullOrEmpty(line) || line.StartsWith("//"))
{
continue;
}
lines++;
}
}
catch { }
finally
{
if (reader != null)
{ reader.Close(); }
}
return lines;
}
}
}