/**
 *  '$RCSfile$'
 *    Purpose: A Class that loads eml-access.xml file containing ACL 
 *             for a metadata document into relational DB
 *  Copyright: 2000 Regents of the University of California and the
 *             National Center for Ecological Analysis and Synthesis
 *    Authors: Jivka Bojilova
 *
 *   '$Author$'
 *     '$Date$'
 * '$Revision$'
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package edu.ucsb.nceas.metacat.accesscontrol;

import java.io.IOException;
import java.io.StringReader;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Stack;
import java.util.Vector;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.xml.sax.Attributes;
import org.xml.sax.ContentHandler;
import org.xml.sax.DTDHandler;
import org.xml.sax.EntityResolver;
import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;
import org.xml.sax.ext.LexicalHandler;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.helpers.XMLReaderFactory;

import edu.ucsb.nceas.metacat.BasicNode;
import edu.ucsb.nceas.metacat.DBEntityResolver;
import edu.ucsb.nceas.metacat.DBSAXNode;
import edu.ucsb.nceas.metacat.DocumentImpl;
import edu.ucsb.nceas.metacat.McdbException;
import edu.ucsb.nceas.metacat.database.DBConnection;
import edu.ucsb.nceas.metacat.database.DBConnectionPool;
import edu.ucsb.nceas.metacat.properties.PropertyService;
import edu.ucsb.nceas.metacat.util.SystemUtil;
import edu.ucsb.nceas.utilities.PropertyNotFoundException;
import edu.ucsb.nceas.utilities.access.AccessControlInterface;

/** 
 * A Class that loads eml-access.xml file containing ACL for a metadata
 * document into relational DB. It extends DefaultHandler class to handle
 * SAX parsing events when processing the XML stream.
 */
public class AccessControlList extends DefaultHandler 
                               implements AccessControlInterface, LexicalHandler
{

 
//  private static String sysdate = DatabaseService.getDBAdapter().getDateTimeFunction();
//  private static String isnull = DatabaseService.getDBAdapter().getIsNULLFunction();
  
  private DBConnection connection;
  private String parserName;
  private Stack elementStack;
  //private String server;
  private String sep;
 
  private boolean	processingDTD;
  private String  user;
  private String[] groups;
  private String  aclid;
  private int     rev;
  private String 	docname;
  private String 	doctype;
  private String 	systemid;

  private String docurl;
  private Vector resourceURL;
  private Vector resourceID;
  private Vector principal;
  private int    permission;
  private String permType;
  private String permOrder;
  private String beginTime;
  private String endTime;
  private int    ticketCount;
  private int    serverCode = 1;

  private Vector aclObjects = new Vector();
  private boolean instarttag = true;
  private String tagName = "";
  
  private static Log logMetacat = LogFactory.getLog(AccessControlList.class);
  /**
   * Construct an instance of the AccessControlList class.
   * It is used by the permission check up from DBQuery or DocumentImpl
   * and from "getaccesscontrol" action
   *
   * @param conn the JDBC connection where acl info is get
   */
  public AccessControlList(DBConnection conn) throws SQLException
  {
    this.connection = conn;
  }
  

  

  /**
   * Construct an instance of the AccessControlList class.
   * It parse acl file and loads acl data into db connection.
   *
   * @param conn the JDBC connection where acl data are loaded
   * @param aclid the Accession# of the document with the acl data
   * @param acl the acl file containing acl data
   * @param user the user connected to MetaCat servlet and owns the document
   * @param groups the groups to which user belongs
   * @param serverCode the serverid from xml_replication on which this document
   *        resides.
   */
  public AccessControlList(DBConnection conn, String aclid, int rev,
                           String user, String[] groups, int serverCode)
                  throws SAXException, IOException, McdbException, PropertyNotFoundException
  {
		String parserName = PropertyService.getProperty("xml.saxparser");
		//this.server = SystemUtil.getSecureServerURL();
		this.sep = PropertyService.getProperty("document.accNumSeparator");

		this.connection = conn;
		this.parserName = parserName;
		this.processingDTD = false;
		this.elementStack = new Stack();

		this.user = user;
		this.groups = groups;
		this.aclid = aclid;
		this.resourceURL = new Vector();
		this.resourceID = new Vector();
		this.principal = new Vector();
		this.permission = 0;
		this.ticketCount = 0;
		// this.publicAcc = null;
		this.serverCode = serverCode;

		// read the access file from db connection
		DocumentImpl acldoc = new DocumentImpl(aclid + sep + rev);
		String acl = acldoc.toString();
		this.rev = acldoc.getRev();

		// Initialize the parse
		XMLReader parser = initializeParser();
		// parse the access file and write the info to xml_access
		if (acl != null) {
			parser.parse(new InputSource(new StringReader(acl)));
		} else {
			throw new McdbException("Could not retrieve access control list for:  " + aclid + sep + rev);
		}
	}

// NOT USED
// /**
// * Construct an instance of the AccessControlList class.
//   * It parses eml-access file and loads acl data into db connection.
//   * It is used from command line execution.
//   *
//   * @param conn the JDBC connection where acl data are loaded
//   * @param docid the Accession# of the document with the acl data
//   * @param aclfilename the name of acl file containing acl data
//   * @param user the user connected to MetaCat servlet and owns the document
//   * @param groups the groups to which user belongs
//   */
//  public AccessControlList( Connection conn, String aclid, String aclfilename,
//                           String user, String[] groups )
//                  throws SAXException, IOException, McdbException
//  {
//    this(conn, aclid, new FileReader(new File(aclfilename).toString()), 
//         user, groups, 1);
//  }
  
  /* Set up the SAX parser for reading the XML serialized ACL */
  private XMLReader initializeParser() throws SAXException 
  {
    XMLReader parser = null;

    // Get an instance of the parser
    parser = XMLReaderFactory.createXMLReader(parserName);

    // Turn off validation
    parser.setFeature("http://xml.org/sax/features/validation", true);
    parser.setProperty("http://xml.org/sax/properties/lexical-handler", this);
      
    // Set Handlers in the parser
    // Set the ContentHandler to this instance
    parser.setContentHandler((ContentHandler)this);

    // make a DBEntityResolver instance
    // Set the EntityReslover to DBEntityResolver instance
    EntityResolver eresolver = new DBEntityResolver(connection,this,null);
    parser.setEntityResolver((EntityResolver)eresolver);
    parser.setDTDHandler((DTDHandler)this);

    // Set the ErrorHandler to this instance
    parser.setErrorHandler((ErrorHandler)this);

    return parser; 
  }
  
  /**
   * Callback method used by the SAX Parser when beginning of the document
   */
  public void startDocument() throws SAXException 
  {
    //delete all previously submitted permissions @ relations
    //this happens only on UPDATE of the access file
    try {
      this.aclObjects = getACLObjects(aclid);

      //delete all permissions for resources related to @aclid if any
      if ( aclid != null ) {
        deletePermissionsForRelatedResources(aclid);
      }
    } catch (SQLException sqle) {
      throw new SAXException(sqle);
    }
  }
  
  /**
   * Callback method used by the SAX Parser when the start tag of an 
   * element is detected. Used in this context to parse and store
   * the acl information in class variables.
   */
  @Override
  public void startElement (String uri, String localName, 
                            String qName, Attributes atts) 
         throws SAXException 
  {
    instarttag = true;
    if(localName.equals("allow"))
    {
      tagName = "allow";
    }
    else if(localName.equals("deny"))
    {
      tagName = "deny";
    }
    BasicNode currentNode = new BasicNode(localName);
    if (atts != null) {
      int len = atts.getLength();
      for (int i = 0; i < len; i++) {
        currentNode.setAttribute(atts.getLocalName(i), atts.getValue(i));
      }
    }
    if ( currentNode.getTagName().equals("acl") ) {
      permOrder = currentNode.getAttribute("order");
    //  publicAcc = currentNode.getAttribute("public");
    }
    elementStack.push(currentNode); 
  }

  /**
   * Callback method used by the SAX Parser when the text sequences of an 
   * xml stream are detected. Used in this context to parse and store
   * the acl information in class variables.
   */ 
  public void characters(char ch[], int start, int length)
         throws SAXException 
  {
    if(!instarttag)
    {
      return;
    }
    String inputString = new String(ch, start, length);
    inputString = inputString.trim(); 
    //System.out.println("==============inputString: " + inputString);
    BasicNode currentNode = (BasicNode)elementStack.peek(); 
    String currentTag = currentNode.getTagName();

      if (currentTag.equals("principal")) {

        principal.addElement(inputString);

      } else if (currentTag.equals("permission")) {

        if ( inputString.trim().toUpperCase().equals("READ") ) {
          permission = permission | READ;
        } else if ( inputString.trim().toUpperCase().equals("WRITE") ) {
          permission = permission | WRITE;
        } else if ( inputString.trim().toUpperCase().equals("CHANGEPERMISSION")) 
        {
          permission = permission | CHMOD;
        } else if ( inputString.trim().toUpperCase().equals("ALL") ) {
          permission = permission | ALL;
        }/*else{
          throw new SAXException("Unknown permission type: " + inputString);
        }*/

      } else if ( currentTag.equals("startDate") && beginTime == null ) {
        beginTime = inputString.trim();

      } else if ( currentTag.equals("stopDate") && endTime == null) {
        endTime = inputString.trim();

      } else if (currentTag.equals("ticketCount") && ticketCount == 0 ) {
        try {
          ticketCount = (new Integer(inputString.trim())).intValue();
        } catch (NumberFormatException nfe) {
          throw new SAXException("Wrong integer format for:" + inputString);
        }
      }
  }

  /**
   * Callback method used by the SAX Parser when the end tag of an 
   * element is detected. Used in this context to parse and store
   * the acl information in class variables.
   */
  public void endElement (String uri, String localName, String qName)
         throws SAXException 
  {
    instarttag = false;
    BasicNode leaving = (BasicNode)elementStack.pop();
    String leavingTagName = leaving.getTagName();

    if ( leavingTagName.equals("allow") ||
         leavingTagName.equals("deny")    ) {
      
      if ( permission > 0 ) {

        // insert into db calculated permission for the list of principals
        try {
          // go through the objects in xml_relation about this acl doc
          for (int i=0; i < aclObjects.size(); i++) {
            // docid of the current object
            String docid = (String)aclObjects.elementAt(i); 
            //DocumentIdentifier docID = new DocumentIdentifier(docid);
            //docid = docID.getIdentifier();
            // the docid get here has revision number, so we need to 
            // remove it.
            //docid = MetacatUtil.getDocIdFromAccessionNumber(docid);
            //System.out.println("The docid insert is!!!!!!!!!! "+ docid);
            insertPermissions(docid,leavingTagName);
          }
          
          // if acl is not in object list
          //should insert permission for aclid itself into database
          /*if (!aclObjects.contains(aclid))
          {
            DocumentIdentifier aclIdItself = new DocumentIdentifier(aclid);
            String aclIdString = aclIdItself.getIdentifier();
            insertPermissions(aclIdString,leavingTagName);
          }*/
          

        } catch (SQLException sqle) {
          throw new SAXException(sqle);
        } catch (Exception e) {
          throw new SAXException(e);
        }
      }

      // reset the allow/deny permission
      principal = new Vector();
      permission = 0;
      beginTime = null;
      endTime = null;
      ticketCount = 0;
    
    }

  }

  /** 
    * SAX Handler that receives notification of DOCTYPE. Sets the DTD.
    * @param name name of the DTD
    * @param publicId Public Identifier of the DTD
    * @param systemId System Identifier of the DTD
    */
  @Override
  public void startDTD(String name, String publicId, String systemId) 
              throws SAXException {
      processingDTD = true;
      logMetacat.debug("AccessControlList.startDTD - Setting processingDTD to true");
      logMetacat.debug("AccessControlList.startDTD - start DTD");
    docname = name;
    doctype = publicId;
    systemid = systemId;
  }
  
  /**
   * SAX Handler that receives notification of end of DTD
   */
  @Override
  public void endDTD() throws SAXException
  {

      processingDTD = false;
      logMetacat.debug("AccessControlList.endDTD - Setting processingDTD to false");
      logMetacat.debug("AccessControlList.endDTD - end DTD");
  }

  /** 
   * SAX Handler that receives notification of the start of entities.
   * @param name name of the entity
   */
  public void startEntity(String name) throws SAXException {
      logMetacat.debug("AccessControlList.startEntity ");
    if (name.equals("[dtd]")) {
        logMetacat.debug("AccessControlList.startEntity  set processingDTD to true.");
      processingDTD = true;
    }
  }

  /** 
   * SAX Handler that receives notification of the end of entities.
   * @param name name of the entity
   */
  public void endEntity(String name) throws SAXException {
      logMetacat.debug("AccessControlList.endEntity ");
    if (name.equals("[dtd]")) {
        logMetacat.debug("AccessControlList.endEntity  set processingDTD to false.");
      processingDTD = false;
    }
  }

  /**
   * Get the document name.
   */
  public String getDocname() {
    return docname;
  }

  /**
   * Get the document processing state.
   */
  public boolean processingDTD() {
    return processingDTD;
  }
  
  /* Get all objects associated with @aclid from db.*/
  private Vector getACLObjects(String aclid) 
          throws SQLException 
  {
    Vector aclObjects = new Vector();
    DBConnection conn = null;
    int serialNumber = -1;
    PreparedStatement pstmt = null;
    try
    {
      //get connection from DBConnectionPool
      conn=DBConnectionPool.getDBConnection("AccessControlList.getACLObject");
      serialNumber=conn.getCheckOutSerialNumber();
      
      // delete all acl records for resources related to @aclid if any
      pstmt = conn.prepareStatement(
                             "SELECT object FROM xml_relation " +
                             "WHERE subject = ? ");
      pstmt.setString(1,aclid);
      pstmt.execute();
      ResultSet rs = pstmt.getResultSet();
      boolean hasRows = rs.next();
      while (hasRows) {
        String object = rs.getString(1);
        //System.out.println("add acl object into vector !!!!!!!!!!!!!!!!!"+object);
        aclObjects.addElement(object);
        hasRows = rs.next();
      }//whil
    }
    catch (SQLException e)
    {
      throw e;
    }
    finally
    {
      try
      {
        pstmt.close();
      }
      finally
      {
        //retrun DBConnection
        DBConnectionPool.returnDBConnection(conn,serialNumber);
      }
    }
    
    return aclObjects;
  }

  /* Delete from db all permission for resources related to @aclid if any.*/
  private void deletePermissionsForRelatedResources(String aclid) 
          throws SQLException 
  {
    //DBConnection conn = null;
    //int serialNumber = -1;
    PreparedStatement pstmt = null;
    try
    {
      //check out DBConenction
      //conn=DBConnectionPool.getDBConnection("AccessControlList.deltePerm");
      //serialNumber=conn.getCheckOutSerialNumber();
    	String sql = "DELETE FROM xml_access WHERE accessfileid = ?";
      // delete all acl records for resources related to @aclid if any
      pstmt = connection.prepareStatement(sql);
      pstmt.setString(1, aclid);
      // Increase DBConnection usage count
      connection.increaseUsageCount(1);
      logMetacat.debug("running sql: " + pstmt.toString());
      pstmt.execute();
      //increase usageCount!!!!!!
      //conn.increaseUsageCount(1);
    }
    catch (SQLException e)
    {
      throw e;
    }
    finally
    {
      pstmt.close();
      //retrun DBConnection
      //DBConnectionPool.returnDBConnection(conn,serialNumber);
    }
  }

  /* Insert into db calculated permission for the list of principals 
   * The DBConnection it is use is class field. Because we want to keep rollback
   * features and it need use same connection
  */
  
  private void insertPermissions(String docid, String permType ) 
                                            throws SQLException 
  {
    PreparedStatement pstmt = null;
    //DBConnection conn = null;
    //int serialNumber = -1;
    try {
      //Check out DBConnection
      //conn=DBConnectionPool.getDBConnection("AccessControlList.insertPerm");
      //serialNumber=conn.getCheckOutSerialNumber();
    	// TODO: look up guid? this is for very old pre-release versions of EML
      String guid = docid;
      pstmt = connection.prepareStatement(
              "INSERT INTO xml_access " + 
              "(guid, principal_name, permission, perm_type, perm_order," +
              "ticket_count, accessfileid) VALUES " +
              "(?,?,?,?,?,?,?)");
      // Increase DBConnection usage count
      connection.increaseUsageCount(1);
      // Bind the values to the query
      pstmt.setString(1, guid);
      pstmt.setInt(3, permission);
      pstmt.setString(4, permType);
      pstmt.setString(5, permOrder);
      pstmt.setString(7, aclid);
      if ( ticketCount > 0 ) {
        pstmt.setInt(6, ticketCount);
      } else {
        pstmt.setInt(6, 0);
      }
      
      //incrase usagecount for DBConnection
      //conn.increaseUsageCount(1);
      String prName;
      for ( int j = 0; j < principal.size(); j++ ) {
        prName = (String)principal.elementAt(j);
        pstmt.setString(2, prName);
        logMetacat.debug("running sql: " + pstmt.toString());
        pstmt.execute();
      /*    
        // check if there are conflict with permission's order
        String permOrderOpos = permOrder;
        int perm = getPermissions(permission, prName, docid, permOrder);
        if (  perm != 0 ) {
          if ( permOrder.equals("allowFirst") ) {
            permOrderOpos = "denyFirst";
          } else if ( permOrder.equals("denyFirst") ) {
            permOrderOpos = "allowFirst";
          }
          throw new SQLException("Permission(s) " + txtValue(perm) + 
                    " for \"" + prName + "\" on document #" + docid +
                    " has/have been used with \"" + permOrderOpos + "\"");
        }
      */
      }
      pstmt.close();

    } catch (SQLException e) {
      throw new 
      SQLException("AccessControlList.insertPermissions(): " + e.getMessage());
    }
    finally
    {
      pstmt.close();
      //return the DBConnection
      //DBConnectionPool.returnDBConnection(conn, serialNumber);
    }
  }

  /* Get permissions with permission order different than @permOrder. */
  private int getPermissions(int permission, String principal,
                             String docid, String permOrder)
          throws SQLException 
  {
    PreparedStatement pstmt = null;
    DBConnection conn = null;
    int serialNumber = -1;
    try
    {
      //check out DBConnection
      conn=DBConnectionPool.getDBConnection("AccessControlList.getPermissions");
      serialNumber=conn.getCheckOutSerialNumber();
      pstmt = conn.prepareStatement(
            "SELECT permission FROM xml_access " +
            "WHERE docid = ? " +
            "AND principal_name = ? " +
            "AND perm_order NOT = ?");
      pstmt.setString(1, docid);
      pstmt.setString(2, principal);
      pstmt.setString(3, permOrder);
      logMetacat.debug("running sql: " + pstmt.toString());
      pstmt.execute();
      ResultSet rs = pstmt.getResultSet();
      boolean hasRow = rs.next();
      int perm = 0;
      while ( hasRow ) {
        perm = rs.getInt(1);
        perm = permission & perm;
        if ( perm != 0 ) {
          pstmt.close();
          return perm;
        }
        hasRow = rs.next();
      }
    }//try
    catch (SQLException e)
    {
      throw e;
    }
    finally
    {
      try
      {
        pstmt.close();
      }
      finally
      {
        DBConnectionPool.returnDBConnection(conn, serialNumber);
      }
    }
    return 0;
  }

    /* Get the int value of READ, WRITE, CHMOD or ALL. */
	public static int intValue(String permission) {
	    
		int thisPermission = 0;
		try
		{
		    thisPermission = new Integer(permission).intValue();
		    if(thisPermission >= 0 && thisPermission <= 7)
		    {
		        return thisPermission;
		    }
		    else
		    {
		        thisPermission = -1;
		    }
		}
		catch(Exception e)
		{ //do nothing.  this must be a word
		}
		
		if (permission.toUpperCase().contains(CHMODSTRING)) {
			thisPermission |= CHMOD;
		} 
		if (permission.toUpperCase().contains(READSTRING)) {
			thisPermission |= READ;
		} 
		if (permission.toUpperCase().contains(WRITESTRING)) {
			thisPermission |= WRITE;
		} 
		if (permission.toUpperCase().contains(ALLSTRING)) {
			thisPermission |= ALL;
		}

		return thisPermission;
	}
  
  /* Get the text value of READ, WRITE, CHMOD or ALL. */
	public static String txtValue(int permission) {
		StringBuffer txtPerm = new StringBuffer();
		
		if ((permission & ALL) == ALL) {
			return ALLSTRING;
		}		
		if ((permission & CHMOD) == CHMOD) {
			txtPerm.append(CHMODSTRING);
		}
		if ((permission & READ) == READ) {
			if (txtPerm.length() > 0)
				txtPerm.append(",");
			txtPerm.append(READSTRING);
		}
		if ((permission & WRITE) == WRITE) {
			if (txtPerm.length() > 0)
				txtPerm.append(",");
			txtPerm.append(WRITESTRING);
		}

		return txtPerm.toString();
	}

  /* Get the flag for public "read" access for @docid from db conn. */
  private String getPublicAccess(String docid) throws SQLException {
    
    int publicAcc = 0;
    PreparedStatement pstmt = null;
    DBConnection conn = null;
    int serialNumber = -1;
    try
    {
      //check out DBConnection
      conn=DBConnectionPool.getDBConnection("AccessControlList.getPublicAcces");
      serialNumber=conn.getCheckOutSerialNumber();
      pstmt = conn.prepareStatement("SELECT public_access FROM xml_documents " +
                                  "WHERE docid = ?");
      pstmt.setString(1, docid);
      pstmt.execute();
      ResultSet rs = pstmt.getResultSet();
      boolean hasRow = rs.next();
      if ( hasRow ) {
        publicAcc = rs.getInt(1);
      }
    
      return (publicAcc == 1) ? "yes" : "no";
    }
    finally
    {
      try
      {
        pstmt.close();
      }
      finally
      {
        DBConnectionPool.returnDBConnection(conn, serialNumber);
      }
    }
  }

  
  /**
   * SAX Handler that receives notification of comments in the DTD
   */
  public void comment(char[] ch, int start, int length) throws SAXException
  {
      logMetacat.trace("AccessControlList.comment - starting comment");
    
  }

  /**
   * SAX Handler that receives notification of the start of CDATA sections
   */
  public void startCDATA() throws SAXException
  {
      logMetacat.trace("AccessControlList.startCDATA - starting CDATA");
  }

  /**
   * SAX Handler that receives notification of the end of CDATA sections
   */
  public void endCDATA() throws SAXException
  {
      logMetacat.trace("AccessControlList.endCDATA - end CDATA");
  }
  
  public static void main(String[] args) {
	  System.out.println("text value for CHMOD (" + CHMOD + "): " + txtValue(CHMOD));
	  System.out.println("text value for READ: (" + READ + "): " + txtValue(READ));
	  System.out.println("text value for WRITE: (" + WRITE + "): " + txtValue(WRITE));
	  System.out.println("text value for ALL: (" + ALL + "): " + txtValue(ALL));
	  int chmod_read = CHMOD|READ;
	  System.out.println("text value for CHMOD|READ: (" + chmod_read + "): " + txtValue(CHMOD|READ));
	  int chmod_write = CHMOD|WRITE;
	  System.out.println("text value for CHMOD|WRITE: (" + chmod_write + "): " + txtValue(CHMOD|WRITE));
	  int chmod_all = CHMOD|ALL;
	  System.out.println("text value for CHMOD|ALL: (" + chmod_all + "): " + txtValue(CHMOD|ALL));
	  int read_write = READ|WRITE;
	  System.out.println("text value for READ|WRITE: (" + read_write + "): " + txtValue(READ|WRITE));
	  int read_all = READ|ALL;
	  System.out.println("text value for READ|ALL: (" + read_all + "): " + txtValue(READ|ALL));
	  int write_all = WRITE|ALL;
	  System.out.println("text value for WRITE|ALL: (" + write_all + "): " + txtValue(WRITE|ALL));
	  int chmod_read_write = CHMOD|READ|WRITE;
	  System.out.println("text value for CHMOD|READ|WRITE: (" + chmod_read_write + "): " + txtValue(CHMOD|READ|WRITE));
	  int chmod_read_all = CHMOD|READ|ALL;
	  System.out.println("text value for CHMOD|READ|ALL: (" + chmod_read_all + "): " + txtValue(CHMOD|READ|ALL));
	  int chmod_write_all = CHMOD|WRITE|ALL;
	  System.out.println("text value for CHMOD|WRITE|ALL: (" + chmod_write_all + "): " + txtValue(CHMOD|WRITE|ALL));
	  int read_write_all = READ|WRITE|ALL;
	  System.out.println("text value for READ|WRITE|ALL: (" + read_write_all + "): " + txtValue(READ|WRITE|ALL));
	  int chmod_read_write_all = CHMOD|READ|WRITE|ALL;
	  System.out.println("text value for CHMOD|READ|WRITE|ALL: (" + chmod_read_write_all + "): " + txtValue(CHMOD|READ|WRITE|ALL));
	  System.out.println();
	  System.out.println("int value for GOOBER: " + intValue("GOOBER"));
	  System.out.println("int value for CHANGEPERMISSION: " + intValue("CHANGEPERMISSION"));
	  System.out.println("int value for READ: " + intValue("READ"));
	  System.out.println("int value for WRITE: " + intValue("WRITE"));
	  System.out.println("int value for ALL: " + intValue("ALL"));
	  System.out.println("int value for CHANGEPERMISSION,READ: " + intValue("CHANGEPERMISSION,READ"));
	  System.out.println("int value for CHANGEPERMISSION,WRITE: " + intValue("CHANGEPERMISSION,WRITE"));
	  System.out.println("int value for CHANGEPERMISSION,ALL: " + intValue("CHANGEPERMISSION,ALL"));
	  System.out.println("int value for READ,WRITE: " + intValue("READ,WRITE"));
	  System.out.println("int value for READ,ALL: " + intValue("READ,ALL"));
	  System.out.println("int value for WRITE,ALL: " + intValue("WRITE,ALL"));
	  System.out.println("int value for CHANGEPERMISSION,READ,WRITE: " + intValue("CHANGEPERMISSION,READ,WRITE"));
	  System.out.println("int value for CHANGEPERMISSION,READ,ALL: " + intValue("CHANGEPERMISSION,READ,ALL"));
	  System.out.println("int value for CHANGEPERMISSION,READ,ALL: " + intValue("CHANGEPERMISSION,WRITE,ALL"));
	  System.out.println("int value for READ,WRITE,ALL: " + intValue("READ,WRITE,ALL"));
	  System.out.println("int value for CHANGEPERMISSION,READ,WRITE,ALL: " + intValue("CHANGEPERMISSION,READ,WRITE,ALL"));
  }

}
