ImageReader::ImageReader( ::File::Reader& rdr )
      : DocumentReader( rdr, "Image", "http://www.XmlCommandLine.org/Image/1.0" )
   {
      ::rapidxml::xml_node<>* ContentNode = root->first_node( "Content" );
      if ( ! ContentNode )
         throw ::std::runtime_error( "The image has no Content node" );

      ::rapidxml::xml_node<>* LayerNode = ContentNode->first_node( "Layer" );
      if ( ! LayerNode )
         throw ::std::runtime_error( "The image has no Layer nodes" );
      while ( LayerNode )
      {
         Layer layer;
         layer.z = ReadNumericAttribute( LayerNode, "z" );
         layer.Width = ReadNumericAttribute( LayerNode, "Width" );
         layer.Height = ReadNumericAttribute( LayerNode, "Height" );

         ::rapidxml::xml_node<>* RowNode = LayerNode->first_node( "Row" );
         if ( ! RowNode )
            throw ::std::runtime_error( "The image has no Row nodes" );
         while ( RowNode )
         {
            Row row;
            row.y = ReadNumericAttribute( RowNode, "y" );

            ::rapidxml::xml_node<>* PixelNode = RowNode->first_node( "Pixel" );
            while ( PixelNode )
            {
               Pixel pixel;
               pixel.x = ReadNumericAttribute( PixelNode, "x" );
               pixel.r = ReadNumericAttribute( PixelNode, "r" );
               pixel.g = ReadNumericAttribute( PixelNode, "g" );
               pixel.b = ReadNumericAttribute( PixelNode, "b" );
               pixel.a = ReadNumericAttribute( PixelNode, "a" );
               row.PixelMap[pixel.x] = pixel;

               PixelNode = PixelNode->next_sibling( "Pixel" );
            }
            layer.RowMap[row.y] = row;

            RowNode = RowNode->next_sibling( "Row" );
         }
         LayerMap[layer.z] = layer;

         LayerNode = LayerNode->next_sibling( "Layer" );
      }
   }
// Start a new element
void CommonReadHandler::startElement(const XMLCh* const uri,const XMLCh* const localname,
                                     const XMLCh* const qname,const Attributes& attrs)
{
    // decode tag name
    char *xName=XMLString::transcode(localname);
    input=NO_INPUT;

    // Handle all elements common between MPM and FEA analysis

    //-------------------------------------------------------
    // <Header> elements
    if(strcmp(xName,"Header")==0)
        block=HEADER;

    else if(strcmp(xName,"Description")==0)
    {   if(block!=HEADER)
            throw SAXException("<Description> must be within the <Header> element.");
        input=TEXT_BLOCK;
        inputID=DESCRIPTION;
    }

    else if(strcmp(xName,"Analysis")==0)
    {   if(block!=HEADER)
            throw SAXException("<Analysis> must be within the <Header> element.");
        input=ANALYSIS_NUM;
    }

    // begin a material
    else if(strcmp(xName,"DevelFlag")==0)
    {   if(block!=HEADER)
            throw SAXException("<DevelFlag> must be within the <Header> element.");
        double flagNumDble=ReadNumericAttribute("Number",attrs,(double)0.0);
        int flagNum=(int)(flagNumDble+0.5);
        if(flagNum<0 || flagNum>=NUMBER_DEVELOPMENT_FLAGS)
            throw SAXException("The <DevelFlag> 'Number' must be from 0 to 9");
        input=INT_NUM;
        inputPtr=(char *)&fmobj->dflag[flagNum];
    }

    else if(strcmp(xName,"ConsistentUnits")==0)
    {   if(block!=HEADER)
            throw SAXException("<ConsistentUnits> must be within the <Header> element.");
        char length[10],mass[10],timeu[10];
        strcpy(length,"");
        strcpy(mass,"");
        strcpy(timeu,"");
        char *aName,*value;
        int i,numAttr=(int)attrs.getLength();
        for(i=0; i<numAttr; i++)
        {   aName=XMLString::transcode(attrs.getLocalName(i));
            value=XMLString::transcode(attrs.getValue(i));
            if(strlen(value)>9)
                throw SAXException("<ConsistentUnits> length, mass, or time attribute is invalid.");
            if(strcmp(aName,"length")==0)
                strcpy(length,value);
            else if(strcmp(aName,"mass")==0)
                strcpy(mass,value);
            else if(strcmp(aName,"time")==0)
                strcpy(timeu,value);
            delete [] aName;
            delete [] value;
        }
        if(strlen(length)==0 && strlen(mass)==0 && strlen(timeu)==0)
        {   strcpy(length,"L");
            strcpy(mass,"M");
            strcpy(timeu,"T");
        }
        if(!UnitsController::SetConsistentUnits(length, mass, timeu))
            throw SAXException("Duplicated <ConsistentUnits> command or one of the units is invalid.");
    }


    //-------------------------------------------------------
    // <Mesh> block

    // Node list
    else if(strcmp(xName,"NodeList")==0)
    {   ValidateCommand(xName,MESHBLOCK,ANY_DIM);
        if(meshType!=UNKNOWN_MESH)
            throw SAXException("<NodeList> can not be used with a generated mesh.");
        block=NODELIST;
        meshType=EXPLICIT_MESH;
        if(theNodes==NULL) theNodes=new NodesController();
    }

    // Element list
    else if(strcmp(xName,"ElementList")==0)
    {   ValidateCommand(xName,MESHBLOCK,ANY_DIM);
        if(meshType!=EXPLICIT_MESH)
            throw SAXException("<ElementList> cannot be used with a generated mesh.");
        block=ELEMENTLIST;
        // MPM only uses when in ElementList (which is uncommon because does not suppport GIMP)
        if(theElems==NULL) theElems=new ElementsController();
    }

    //-----------------------------------------------------------
    // <GridBCs> section

    // DisplacementBC section
    else if(strcmp(xName,"DisplacementBCs")==0)
    {   ValidateCommand(xName,GRIDBCHEADER,ANY_DIM);
        block=FIXEDNODES;
    }

    //-------------------------------------------------------
    // <Thermal> section

    // begin Thermal section
    else if(strcmp(xName,"Thermal")==0)
    {   block=THERMAL;
    }

    //-------------------------------------------------------
    // <Material> Definitions

    // begin a material
    else if(strcmp(xName,"Material")==0)
    {   block=MATERIAL;
        int matID=0;		// invalid ID unless it is set
        char matName[200],*aName,*value;
        matName[0]=0;		// to get the name of the material
        int i,numAttr=(int)attrs.getLength();
        for(i=0; i<numAttr; i++)
        {   aName=XMLString::transcode(attrs.getLocalName(i));
            value=XMLString::transcode(attrs.getValue(i));
            if(strcmp(aName,"Type")==0)
                sscanf(value,"%d",&matID);
            else if(strcmp(aName,"Name")==0)
            {   if(strlen(value)>199) value[200]=0;
                strcpy(matName,value);
            }
            delete [] aName;
            delete [] value;
        }
        if(strlen(matName)==0)
            throw SAXException("<Material> must be named using 'Name' atttribute.");
        if(!matCtrl->AddMaterial(matID,matName))
            ThrowCatErrorMessage("Invalid material: either undefined type or not allowed for current analysis type",matName);
    }

    // begin a material
    else if(strcmp(xName,"color")==0)
    {   if(block!=MATERIAL)
            throw SAXException("<color> must be within a <Material> definition.");
        float redClr=-1.,greenClr=-1.,blueClr=-1.,alpha=1.;
        char *aName,*value;
        int i,numAttr=(int)attrs.getLength();
        for(i=0; i<numAttr; i++)
        {   aName=XMLString::transcode(attrs.getLocalName(i));
            value=XMLString::transcode(attrs.getValue(i));
            if(strcmp(aName,"red")==0)
                sscanf(value,"%f",&redClr);
            else if(strcmp(aName,"green")==0)
                sscanf(value,"%f",&greenClr);
            else if(strcmp(aName,"blue")==0)
                sscanf(value,"%f",&blueClr);
            else if(strcmp(aName,"alpha")==0)
                sscanf(value,"%f",&alpha);
            delete [] aName;
            delete [] value;
        }
        if(redClr>0.)
        {   if(redClr>1.) redClr=1.;
            if(greenClr<0.) greenClr=redClr;
            if(blueClr<0.) blueClr=redClr;
            if(greenClr>1.) greenClr=1.;
            if(blueClr>1.) blueClr=1.;
            if(alpha<0.) alpha=0.;
            if(alpha>1.) alpha=1.;
            matCtrl->SetMatColor(redClr,greenClr,blueClr,alpha);
        }
    }

    //-------------------------------------------------------
    // Analysis-specific elements
    else if(!myStartElement(xName,attrs))
    {   // look for material property
        if(block==MATERIAL)
        {   inputPtr=matCtrl->InputPointer(xName,input,gScaling);
            if(inputPtr==NULL)
            {   char msg[255];
                strcpy(msg,"Unrecognized ");
                strcat(msg,matCtrl->MaterialType());
                strcat(msg," material property was found");
                ThrowCatErrorMessage(msg,xName);
            }
        }

        // or an invalid tag
        else if(!(strcmp(xName,"JANFEAInput")==0))
            ThrowCatErrorMessage("Unrecognized input element found",xName);
    }


    // delete tag string
    delete [] xName;
}