Thursday, December 4, 2014

Contracts in a CAB

Team. The contracts have been placed in the controller application. If a contract is violated, we raise an exception which the main method catches. The source code is below. I would appreciate any insights or comments. Also, please forgive another skipped day, Wednesday, 12.03.2012. I am wrapping up an assignment with a small business client whose billing cycle is just ending. So, I have been very busy. La-La.

//BEGIN JAVA CAB SOURCE
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cab;
/**
 * The CAB eventually will be the kernel of the CABOOSE general purpose servlet.
 * Implicit Contract Requirements and Guidelines:
 * The CAB contracts with support classes that have rendering methods which
 * return String content. These methods also must have the signature
 *  public String methodName( java.util.HashMap<String,String> aMapParameter )
 * by implicit contract. If they do not, the reflection activities which
 * automatically invoke them will fail since they cannot find the requested
 * method. Class, method, tile replacement, and stencil filename identifiers are
 * all stored in a file called directory.xml which contain a mapping between
 * each item.
 *
 * @author Jody Sharpe
 */
public class CAB {
    //Simulated URL String. We will eventually get all of our input from the
    //the servlet's Request object.
    String request = "?project=caboose&module=skeleton&create_date=10.30.2014";
    //A data structure for holding a description of the page, stencil, and tile
    //directory. It is keyed by the page name. Each value in the structure is a
    //ViewModel. See the description of this inner class for information on its
    //content and structure.
    java.util.HashMap<String,ViewModel> aDirectoryMap = null;
   
    //private inner class representing a model of a single view which the
    //controller can return.
    private class ViewModel {
        //The stencil filename.
        private String theStencilName = null;
        //A data structure which holds the tile identifier as a key and the name
        //of the class and method for rendering the the tile as a
        //comma seperated value
        private java.util.HashMap<String,String> theTileRendererMappings = null;
       
        //This method assigns the stencil filename
        void settingTheStencilName( String aStencilName ) throws Exception{
            //Opening Contract
            //The stencil name is not a null or empty string.
            if ( aStencilName == null || aStencilName.length() == 0 ){
                throw new Exception("Stencil name is null or empty");
            }
            theStencilName = aStencilName;           
            //Closing Contract (Implicit)
            //The stencil name is a non-empty string.
        }
       
        //This method assigns the tile renderer mappings
        void settingTheTileRendererMappings( java.util.HashMap<String,String> aSetOfTileRendererMappings ) throws Exception{
            //Opening Contract
            //The set of mappings between tiles and renderer methods is not null
            //It might be of size zero if there soes not exist any replacment tiles
            //within the stencil.
            if ( aSetOfTileRendererMappings == null ){
                throw new Exception("The mapping between tiles and render methods is null");
            }           
            theTileRendererMappings = aSetOfTileRendererMappings;           
            //Closing Contract (Implicit)
            //The tile renderer mappings is not null. They might be empty.
        }
       
        //This method returns the stencil file name
        String gettingTheStencilName(){
            //Invariant Contract (Implicit)
            //The stencil name is a non-empty string.
            return( theStencilName );           
        }
       
        //This method retruns the tile renderer mappings data structure
        java.util.HashMap<String,String> gettingTheTileRendererMappings(){
            //Invariant Contract (Implicit)
            //The tile renderer mappings are not null. They might be empty.
            return( theTileRendererMappings );           
        }
    }
   
    //This method extracts the content of a stencil from the file system based
    //upon a file name. In the future this method will need adapting so it
    //retrieves the proper file in a web server's file system using
    //the servlet context.
    String gettingTheResponseStencil(String aViewFileName) throws Exception {
        //Opening Contract
        //The file name of the view stencil is not a null or empty string.
        if ( aViewFileName == null || aViewFileName.length() == 0 ){
            throw new Exception("The filename of the view stencil is null or empty");
        }           
        java.io.BufferedReader aStencilReader = new java.io.BufferedReader(new java.io.FileReader(aViewFileName));
        String theStencilContent = "", theNextLine = null;
        while ((theNextLine = aStencilReader.readLine()) != null) {
            theStencilContent += theNextLine;
        }
        //Closing Contract
        //The stencil content of the view in not its initial value of an empty string.
        if ( theStencilContent.length() == 0 ){
            throw new Exception("The stencil content of the view stencil is empty");
        }                   
        return (theStencilContent);
    }
    //This method extracts the name and value pairs from the input query string.
    //We assume that it is not url encoded. For the generic servlet edition of
    //the cab, this method will not be useful. But it might be of use for a
    //command-line based edition of the cab for report writing or rendering
    //windowing applications.
    java.util.HashMap<String, String> mappingTheRequestNameValuePairs(String aRequest) throws Exception{
        java.util.HashMap<String, String> aMap = new java.util.HashMap<String, String>();
        //Opening Contract
        //If the request query string is null or empty, return an empty aMap
        if ( aRequest == null || aRequest.length() == 0 ){
            return aMap;
        }                   
        aRequest = aRequest.replace("?", "");
        String[] theNameValuePairs = aRequest.split("&");
        for (String aNameValuePair : theNameValuePairs) {
            final int theName = 0, theValue = 1;
            String[] aTuple = aNameValuePair.split("=");
            aMap.put(aTuple[theName], aTuple[theValue]);
        }
        //Closing Contract
        //The Map cannot empty on the input of a non-empty request query string
        if ( aMap.size() == 0){
            throw new Exception("The input key-value map is empty. Could not interpret the query string.");
        }
        return aMap;
    }
    //This model populates a view stencil with the content generated by
    //dynamically invoking reendering methods and replacing their associated
    //user-defined tile identifiers ( a.k.a stencil replacement variables ).
    //See the contract guideline in the header comments of this document.
    String preparingTheResponse(String theViewId, java.util.HashMap<String, String> aRequestMap) throws Exception {
    
        //Opening Contract
        //The id of the view cannot be null or empty
        if ( theViewId == null || theViewId.length() == 0 ){
                throw new Exception("The view identifier is null or empty.");
        }                   
       
        //Opening Contract (Implicit)
        //The size of the request map is zero or larger
    
        ViewModel aViewModel = (ViewModel) aDirectoryMap.get( theViewId );
       
        //Intermediate Contract
        //The view model cannot be null.
        if ( aViewModel == null ){
            throw new Exception("The view model was not found.");
        }
       
        String [] aTileClassAndMethod = null;       
       
        String theStencilContent = gettingTheResponseStencil( aViewModel.gettingTheStencilName() );
        //Intermediate Contract (Implicit)
        //The stencil content of the view in not an empty string.
       
        java.util.HashMap<String,String> someTileRenderMappings = aViewModel.gettingTheTileRendererMappings();
       
        //Intermediate Contract (Implicit)
        //The tile renderer mappings are not null. They might be empty.
       
        for( String aTileId : someTileRenderMappings.keySet()){       
           
            //Intermediate Contract Within Evaluation (.get) (Implicit)
            //The tile class and method is a non-empty string whcih contains a single
            //comma as a CSV string.
       
            aTileClassAndMethod = ((String)someTileRenderMappings.get( aTileId )).split(",");                                   
       
            //Intermediate Contract
            //The class and method names for the tile renderer cannot be null or empty.
            if ( aTileClassAndMethod[0] == null || aTileClassAndMethod[1] == null || aTileClassAndMethod[0].length() == 0 || aTileClassAndMethod[1].length() == 0 ){
                throw new Exception("The class or method name for the tile renderer was null or empty.");
            }
       
            //Start Reflection Activities
            Class aClass = Class.forName( aTileClassAndMethod[0] );
            Class [] theFullyQualifiedParameterTypeClasses = new Class[1];
            theFullyQualifiedParameterTypeClasses[0] = (new java.util.HashMap<String,String>()).getClass();
            Object [] theParameterObjectList= new Object [1];                                   
            theParameterObjectList[0] = aRequestMap;
            theStencilContent = theStencilContent.replace( aTileId , (String) ( aClass.getMethod(aTileClassAndMethod[1],theFullyQualifiedParameterTypeClasses) ).invoke((aClass.getConstructor()).newInstance(), theParameterObjectList)); 
            //End Reflection Activities                        
           
        }
     
       //Closing Contract (Implicit)
       //The class and method for the given name exist among the controller application bundle.     
        return (theStencilContent);
    }
    //This model extracts the information from the directory.xml file using an
    //XML reader that creates a DOM from which the method creates a user-defined
    //map between view names and ViewModel objects. In the future, we might
    //utilize a SAX XML reader skipping the creation of the DOM and directly
    //create the ViewModel map data structure. This should result in a slight
    //processing speed up although we will only be during it once during a servlet
    //session and storing the utlimate view model map in the servlet's session
    //object. Directoy.xml represents a model mapping for the entire application.
    //Based upon the length of this method ( 50+ lines of code ) it should
    //likely be subdivided and invoke a few private utility methods for
    //completing its work.
    void readingTheDirectoryXMLFile(String theViewId) throws Exception {
        
        //Opening Contract
        //The id of the view cannot be null or empty
        if ( theViewId == null || theViewId.length() == 0 ){
                throw new Exception("The view identifier is null or empty.");
        }
       
        String aViewId = null;
        String aStencil = null;
        String aTileId = null;
        String aTileClass = null;
        String aTileMethod = null;
       
        aDirectoryMap = new java.util.HashMap<String, ViewModel>();
       
        javax.xml.parsers.DocumentBuilderFactory theDocumentBuilderFactory =
                javax.xml.parsers.DocumentBuilderFactory.newInstance();
        javax.xml.parsers.DocumentBuilder theDocumentBuilder = theDocumentBuilderFactory.newDocumentBuilder();
        //Intermediate Contract (implicit)
        //The argument for the java.io.File constructor is the canonical name
        //for all CABOOSE bundles and is XML. (It will differ in the final version).
        java.io.File aFile = new java.io.File("./src/cab/directory.xml");
        org.w3c.dom.Document theDocument = theDocumentBuilder.parse(aFile);
        theDocument.getDocumentElement().normalize();
        org.w3c.dom.NodeList theModelNodeList = theDocument.getElementsByTagName("model");
        for (int aModelNodeIndex = 0; aModelNodeIndex < theModelNodeList.getLength(); aModelNodeIndex++) {
            org.w3c.dom.Node aModelNode = theModelNodeList.item(aModelNodeIndex);
            if (aModelNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
                org.w3c.dom.Element aModelElement = (org.w3c.dom.Element) aModelNode;
                org.w3c.dom.NodeList theViewNodeList =
                        aModelElement.getElementsByTagName("view");
                for (int aViewNodeIndex = 0; aViewNodeIndex < theViewNodeList.getLength(); aViewNodeIndex++) {
                    org.w3c.dom.Node aViewNode = (org.w3c.dom.Node) theViewNodeList.item(aViewNodeIndex);
                    if (aViewNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
                        org.w3c.dom.Element aViewElement = (org.w3c.dom.Element) aViewNode;
                        ViewModel aViewModel = new ViewModel();
                       
                        aViewId = aViewElement.getAttribute("id");
                       
                        aStencil = aViewElement.getAttribute("stencil");
                       
                        aViewModel.settingTheStencilName(aStencil);
                        java.util.HashMap<String,String> someTileRendererMappings = new java.util.HashMap<String,String>();
                        org.w3c.dom.NodeList theTileNodeList = aViewElement.getElementsByTagName("tile");
                            for (int aTileNodeIndex = 0; aTileNodeIndex < theTileNodeList.getLength(); aTileNodeIndex++) {
                               
                                org.w3c.dom.Node aTileNode = (org.w3c.dom.Node) theTileNodeList.item(aTileNodeIndex);
                                if (aTileNode.getNodeType() == org.w3c.dom.Node.ELEMENT_NODE) {
                                    org.w3c.dom.Element aTileElement = (org.w3c.dom.Element) aTileNode;
                                   
                                    aTileId = aTileElement.getAttribute("id");
                                    aTileClass = aTileElement.getAttribute("class");
                                    aTileMethod = aTileElement.getAttribute("method");
                                   
                                    someTileRendererMappings.put( aTileId, aTileClass + "," + aTileMethod );
                                   
                                }
                               
                            }            
                            aViewModel.settingTheTileRendererMappings(someTileRendererMappings);
                                aDirectoryMap.put(aViewId, aViewModel);
                           
                    }
                }
            }
        }  
        //Closing Contract
        //The directory amp cannot be empty if the CAB returns a view
        if ( aDirectoryMap.size() == 0 ){
                throw new Exception("The application view directory map is empty.");
        }
    }
    /**
     * @param args the command line arguments
     * This is the main entry point of the application. It builds the request
     * map from the the "temporary" query string. It then build the view model
     * mappings from the directory.xml file by side-effect. Finally it prepares
     * and presents a response. All in less than 0.5 KLOC including
     * the comments. The number of lines of code are not critical in application.
     * The features it provides in the lines it has is most important.
     */
    public static void main(String[] args) {
        // TODO code application logic here
        CAB aCAB = new CAB();
        try {       
            java.util.HashMap<String, String> aRequestMap = aCAB.mappingTheRequestNameValuePairs(aCAB.request);
            aCAB.readingTheDirectoryXMLFile("test_page");
            System.out.println(aCAB.preparingTheResponse("test_page", aRequestMap));
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//END JAVA CAB SOURCE

No comments:

Post a Comment