public class User implements Serializable {
    
    private static final long serialVersionUID = -9105054849954016390L;
    private static final Logger logger = Logger.getLogger(User.class);
    
	/** Distinguished name */
	private String dn = null;
	/** Common name */
	private String cn = null;
	/** Password of the User. Could be cleartext, encoded or hash depending on implementation. */
    private String password = null;
    /** Unique identifier */
    private String uid = null;
    
    /** First name (given name) */ 
    private String firstName = null;
    /** Last name (surname) */
    private String lastName = null;
    /** Description of the User */
    private String description = null;
    /** e-mail address of the User */
    private String mail = null;
    /** Preferred language of the User */
    private String preferredLanguage = null;
    /** Title  of the User */
    private String title = null;
    /** Employee number  of the User */
    private String employeeNumber = null;
    /** Jpeg photo  of the User. Could be URI or file address depenging on implementation */
	private String jpegPhoto = null;
	/** Role for fedora commons repository */
	private Vector<Role> fedoraRoles = null;
	/** User's organization */
    private Organization organization = null;
    /** User's group in organization */
    private String group = null;
	/** Locale of the User */
	private Locale locale = null;
	/** User is anonymous */
	private boolean anonymous = false;
	/** User is instance admin */
	private boolean instance_admin = false;
	
	private int highestAccessRight = 0;
	
	/**
     * Constructor
     */
	public User() {
	    
	}
	
	/**
     * Constructor
     */
	public User(String dn, Locale language) {
	    
        this.setDn(dn);
        
        // get user attributes from ldap
	    HashMap<String,String> usrAttrs = Osa.authManager.getAttributes(dn);
	   
	    if (dn.equals(Osa.authManager.getInstanceAdminDn())) {
	        this.setInstanceAdmin(true);
	    } 
	    
	    if (usrAttrs.containsKey("cn")) {
            this.setCn(usrAttrs.get("cn"));
            this.setAnonymous(false);
            logger.info("logged in with dn: "+dn);
        } else {
            this.setAnonymous(true);
            logger.info("logged in as anonymous user with dn: "+dn);
        }
	    
        if (usrAttrs.containsKey("preferredLanguage")) {
            String[] localeParts = usrAttrs.get("preferredLanguage").split("_");
            this.locale = new Locale(localeParts[0],localeParts[1]);
        }
        
        // TODO: find better way to do this
        // parse organization name from dn
	    String[] parts = dn.split(",");
	    String orgname = "";
	    for (int i=0; i<parts.length; i++) {
	    	if (parts[i].indexOf("o=") == 0) {
	    		orgname = parts[i].replace("o=", "");
	    		break;
	    	}
	    }
	    
	    Organization org = new Organization(dn.replaceAll("(cn=.*?,)", ""), orgname, null);
	    this.setOrganization(org);

	    if (usrAttrs.containsKey("mail")) {
	        this.setMail(usrAttrs.get("mail"));
	    }
	    
	    if (anonymous) {
	        Role anonymousRole = Osa.authManager.getPublicRole(orgname);
	        Vector<Role> anonymousRoles = new Vector<Role>();
	        anonymousRoles.add(anonymousRole);
	        this.setFedoraRoles(anonymousRoles);
	        logger.info("User role in Fedora: "+anonymousRole.name);
	        
	        if (language != null) {
	            this.locale = language;
	        }
	        
	    } else if (!this.isInstanceAdmin()) {
	        this.setFedoraRoles(Osa.authManager.getUserRoles(dn, org.getName()));
	        for (Role role : this.getFedoraRoles()) {
	            logger.info("User role in Fedora: "+role.name);
	        }
	    }
	    
	    logger.info("User rolelevel: "+this.getHighestAccessRight());	   
	    logger.info("User locale: "+this.locale);
	    logger.info("User organization: "+this.organization.getName());
	}
	
	/**
     * @return the application role
     */
	public String getRole() {
	    int acRight = this.getHighestAccessRight();
	    String role = "";
	    
	    if (this.isInstanceAdmin()) {
	        role = "instance_admin";
	    } else if (acRight == AccessRight.ACCESSRIGHTLEVEL_ADMIN) {
	        role = "admin";
	    } else if (acRight == AccessRight.ACCESSRIGHTLEVEL_RECORDSMANAGEMENT) {
	        role = "manager";
	    } else {
	        role = "reader";
	    }
	    return role;
	}

	
	/**
	 * @return the dn
	 */
	public String getDn() {
		return dn;
	}
	/**
	 * @param dn the dn to set
	 */
	public void setDn(String dn) {
		this.dn = dn;
	}
	/**
	 * @return the cn
	 */
	public String getCn() {
		return cn;
	}
	/**
	 * @param cn the cn to set
	 */
	public void setCn(String cn) {
		this.cn = cn;
	}
	/**
	 * @return the password
	 */
	public String getPassword() {
		return password;
	}
	/**
	 * @param password the password to set
	 */
	public void setPassword(String password) {
		this.password = password;
	}
	/**
	 * @return the uid
	 */
	public String getUid() {
		return uid;
	}
	/**
	 * @param uid the uid to set
	 */
	public void setUid(String uid) {
		this.uid = uid;
	}
	/**
	 * @return the firstName
	 */
	public String getFirstName() {
		return firstName;
	}
	/**
	 * @param firstName the firstName to set
	 */
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	/**
	 * @return the lastName
	 */
	public String getLastName() {
		return lastName;
	}
	/**
	 * @param lastName the lastName to set
	 */
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	/**
	 * @return the description
	 */
	public String getDescription() {
		return description;
	}
	/**
	 * @param description the description to set
	 */
	public void setDescription(String description) {
		this.description = description;
	}
	/**
	 * @return the mail
	 */
	public String getMail() {
		return mail;
	}
	/**
	 * @param mail the mail to set
	 */
	public void setMail(String mail) {
		this.mail = mail;
	}
	/**
	 * @return the preferredLanguage
	 */
	public String getPreferredLanguage() {
		return preferredLanguage;
	}
	/**
	 * @param preferredLanguage the preferredLanguage to set
	 */
	public void setPreferredLanguage(String preferredLanguage) {
		this.preferredLanguage = preferredLanguage;
	}
	/**
	 * @return the title
	 */
	public String getTitle() {
		return title;
	}
	/**
	 * @param title the title to set
	 */
	public void setTitle(String title) {
		this.title = title;
	}
	/**
	 * @return the employeeNumber
	 */
	public String getEmployeeNumber() {
		return employeeNumber;
	}
	/**
	 * @param employeeNumber the employeeNumber to set
	 */
	public void setEmployeeNumber(String employeeNumber) {
		this.employeeNumber = employeeNumber;
	}
	/**
	 * @return the jpegPhoto
	 */
	public String getJpegPhoto() {
		return jpegPhoto;
	}
	
	/**
	 * Get all access right filters from user roles
	 * @return
	 */
	public Vector< HashMap<String, String> > getAccessRightFilters() {
	    // Collect filters per role to vector
	    Vector< HashMap<String, String> > filters = new Vector< HashMap<String, String> >();
	    // vector to store paths, where access is denied
        Vector<String> nonAccessiblePaths = new Vector<String>();
        
        if (this.isInstanceAdmin()) {
            return filters;
        }
        
        // Check if access is denied
        for (Role role : this.getFedoraRoles()) {
            for (AccessRight acRight : role.getRoleAccessrights()) {
                int accessRightLevel = acRight.getAccessRightLevel();
                if (accessRightLevel == AccessRight.ACCESSRIGHTLEVEL_DENY_META) {
                    // access denied to this path
                    String accessRightPath = "("+acRight.getAccessRightPath()+"";
                    // replace '+' char with '*' for Solr. Query syntax: ("publicityLevel AND accessRightPath/"*)
                    if (accessRightPath.contains("+")) accessRightPath = accessRightPath.replace("+", "") + "*";
                    accessRightPath += ")";                    
                    nonAccessiblePaths.add(accessRightPath);
                }
            }
        }
        
        // Go through all roles
	    for (Role role : this.getFedoraRoles()) {
            // Go through all AccessRights of the Role
            for (AccessRight acRight : role.getRoleAccessrights()) {
                String publicityLevels = "(" + acRight.getPublicityLevels() + ")";
                String accessRightPath = "(" + acRight.getAccessRightPath() + "";
                // replace '+' char with '*' for Solr. Query syntax: ("publicityLevel AND accessRightPath/"*)
                if (accessRightPath.contains("+")) accessRightPath = accessRightPath.replace("+", "") + "*";
                accessRightPath += ")";
                int accessRightLevel = acRight.getAccessRightLevel();
                HashMap<String, String> filter = new HashMap<String, String>();

                if (accessRightLevel != AccessRight.ACCESSRIGHTLEVEL_DENY_META) {
                    filter.put("c.accessRights", publicityLevels);
                    filter.put("m.objectPath", accessRightPath);
                    
                    // add paths, where access is denied, to filter
                    for (String path : nonAccessiblePaths) {
                        filter.put("-m.objectPath", path);
                    }
                    filters.add(filter);
                }
            }
        }
        return filters;
    }
	
    /**
     * @param User's highest accessright level to set
     */
    public void setHighestAccessRight(int value) {
        this.highestAccessRight = value;
    }
    
	/**
	 * @return User's highest accessright
	 */
    public int getHighestAccessRight() {
        int value = 0;
        if (this.highestAccessRight == 0) {
            if (this.isInstanceAdmin()) {
                value = AccessRight.ACCESSRIGHTLEVEL_INSTANCEADMIN;
            } else  {
                for (Role role : this.getFedoraRoles()) {
                    for (AccessRight acRight : role.getRoleAccessrights()) {
                        if (acRight.getAccessRightLevel() > value) {
                            value = acRight.getAccessRightLevel();
                        }
                    }
                }
            }
            setHighestAccessRight(value);
        }
        return this.highestAccessRight;
    }
	
    /**
     * @return the roles determined to fedora
     */
    public Vector<Role> getFedoraRoles() {
    	return this.fedoraRoles;
    }
    /**
     * @param roles to the roles set
     */
    public void setFedoraRoles(Vector<Role> roles) {
    	this.fedoraRoles = roles;
    }
	/**
	 * @param jpegPhoto the jpegPhoto to set
	 */
	public void setJpegPhoto(String jpegPhoto) {
		this.jpegPhoto = jpegPhoto;
	}
	/**
	 * @return the organization
	 */
	public Organization getOrganization() {
		return organization;
	}
	/**
	 * @param organization the organization to set
	 */
	public void setOrganization(Organization organization) {
		this.organization = organization;
	}
	/**
     * @return group
     */
	public String getGroup() {
        return this.group;
    }
	/**
     * @param group the group to set
     */
	public void setGroup(String group) {
	    this.group = group;
	}
	/**
	 * @return the locale
	 */
	public Locale getLocale() {
		return locale;
	}
	/**
	 * @param locale the locale to set
	 */
	public void setLocale(Locale locale) {
		this.locale = locale;
	}

	public String toString() {    
        ReflectionToStringBuilder tsb = 
        	new ReflectionToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
        return tsb.toString();
    }

	public void setAnonymous(boolean anonymous) {
		this.anonymous = anonymous;
	}

	public boolean isAnonymous() {
		return anonymous;
	}
	
	public void setInstanceAdmin(boolean value) {
	    this.instance_admin = value;
	}
	
	public boolean isInstanceAdmin() {
        return this.instance_admin;
    }
	
	public boolean isAdmin() {
	    return this.getHighestAccessRight() >= AccessRight.ACCESSRIGHTLEVEL_ADMIN;
    }

} public class CaptureBean extends DataStream {
    
    private static final long serialVersionUID = 1L;
    private static final Logger logger = Logger.getLogger(CaptureBean.class);
    
    public static final String CAPTURE_DSID 			= "CAPTURE";
    public static final String CAPTURE_LABEL 			= "Capture Record";
    
    public static final String RELATION_ConformsTo 		= "conformsTo";
    public static final String RELATION_HasAction 		= "hasAction";    
    public static final String RELATION_HasAgent 		= "hasAgent";
    public static final String RELATION_HasEvent 		= "hasEvent";
    public static final String RELATION_HasFormat 		= "hasFormat";
    public static final String RELATION_HasPart 		= "hasPart";
    public static final String RELATION_HasPlace 		= "hasPlace";
    public static final String RELATION_HasVersion 		= "hasVersion";
    public static final String RELATION_IsFormatOf 		= "isFormatOf";
    public static final String RELATION_IsChildOf 		= "isChildOf";
    public static final String RELATION_IsParentOf 		= "isParentOf";
    public static final String RELATION_IsPartOf 		= "isPartOf";
    public static final String RELATION_IsReferencedBy 	= "isReferencedBy";
    public static final String RELATION_IsReplacedBy 	= "isReplacedBy";
    public static final String RELATION_IsRequiredBy 	= "isRequiredBy";
    public static final String RELATION_IsVersionOf 	= "isVersionOf";
    public static final String RELATION_References 		= "references";
    public static final String RELATION_Replaces 		= "replaces";
    public static final String RELATION_Requires 		= "requires";
    public static final String RELATION_Source 			= "source";
    public static final String RELATION_HasRedaction 	= "hasRedaction";
    public static final String RELATION_IsRedactionOf 	= "isRedactionOf";
    public static final String RELATION_IsMemberOf 		= "isMemberOf";
    
    public static final String PUBLICITY_LEVEL          = "accessRights";
    public static final String IDENTIFIER               = "id";
    public static final String PREFERREDNAME            = "preferredName";
    public static final String TITLE                    = "title";
    public static final String TYPE 			        = "type";
    
    // Constructor
    public CaptureBean(String PID) {
        super(PID);
        dsID = CaptureBean.CAPTURE_DSID;
    }
    
    /**
     * Returns the value of isPartOf-relation
     * @return
     */
    public String getParent() {
        String parent = null;
        MetaDataElement element = this.getMetaDataElements().get(CaptureBean.RELATION_IsPartOf);
        
        if (element != null) {
            parent = element.getValue();
        }
        return parent;
    }
    
    public String getPublicityLevel() {
        String parent = null;
        MetaDataElement element = this.getMetaDataElements().get(CaptureBean.PUBLICITY_LEVEL);
        
        if (element != null) {
            parent = element.getValue();
        }
        return parent;
    }
        
    /**
     * set metadata to capture datastream
     * @param name      name of the relation
     * @param value     value  of the relation
     * @param eType     type of metadata 
     */
    public void setMetaDataElement(String name, String visibleName, String value, MetaDataType eType) {
        
        if (name.equalsIgnoreCase(CaptureBean.RELATION_ConformsTo)
            || name.equalsIgnoreCase(CaptureBean.RELATION_HasFormat)
            || name.equalsIgnoreCase(CaptureBean.RELATION_HasPart)
            || name.equalsIgnoreCase(CaptureBean.RELATION_HasRedaction)
            || name.equalsIgnoreCase(CaptureBean.RELATION_HasVersion)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsChildOf)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsFormatOf)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsMemberOf)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsParentOf)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsPartOf)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsRedactionOf)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsReferencedBy)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsReplacedBy)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsRequiredBy)
            || name.equalsIgnoreCase(CaptureBean.RELATION_IsVersionOf)
            || name.equalsIgnoreCase(CaptureBean.RELATION_References)
            || name.equalsIgnoreCase(CaptureBean.RELATION_Replaces)
            || name.equalsIgnoreCase(CaptureBean.RELATION_Requires)
            || name.equalsIgnoreCase(CaptureBean.RELATION_Source)
                )
            {
                super.setMetaDataElement(name, visibleName, value, MetaDataElement.MetaDataType.relation);
            } else {
                super.setMetaDataElement(name, visibleName, value, eType);
            }
    }
    
} public class FedoraBean {
    
    private static final Logger logger = Logger.getLogger(FedoraBean.class);
    public static final String DATASTREAM_DC = "DC";
    public static final String DATASTREAM_CAPTURE = "CAPTURE";
    public static final String DATASTREAM_CONTENT = "CONTENT";
    public static final String DATASTREAM_AUDIT_TRAIL = "AUDIT_TRAIL";
    public static final String DATASTREAM_RELS_EXT = "RELS-EXT";
    public static final String DATASTREAM_RELS_INT = "RELS-INT";
    public static final String DATASTREAM_THUMB = "THUMB";
    public static final String DATASTREAM_ATTACHMENT = "ATTACHMENT";
    public static final String DATASTREAM_MANAGEMENT = "MANAGEMENT";
    
    public static final String DATASTREAM_ORIGINAL = "ORIGINAL";
    public static final String DATASTREAM_ORIGINAL_LABEL = "-O";
    public static final String DATASTREAM_THUMB_LABEL = "-P";
    public static final String DATASTREAM_ATTACHMENT_LABEL = "-A";
    
    public static final String OBJ_LABEL        = "objLabel";
    public static final String OBJ_CONTENTMODEL = "model";
    public static final String OBJ_CREATIONDATE = "objCreateDate";
    public static final String OBJ_LASTMODIFIED = "objLastModDate";
    public static final String OBJ_OWNERID      = "objOwnerId";
    public static final String OBJ_STATE        = "objState";
    public static final String OBJ_DISSINDEX    = "objDissIndexViewURL";
    public static final String OBJ_ITEMINDEX    = "objItemIndexViewURL";

    // datastreams
    DCBean dublinCoreData = null;
    CaptureBean captureData = null;
    OriginalBean original = null;
    ContentBean content = null;
    RelationsBean relations = null;
    ThumbBean thumb = null;
    AttachmentBean attachment = null;
    ManagementBean management = null;
    
    // common object data
    private String objectLabel;
    private String objectContentModel;
    private String objectCreationDate;
    private String objectLastModified;
    private String objectOwnedId;
    private DataStream.State eObjectState;
    
    public String PID;    
    public String type; // document, image, etc.
    public String oldParent;
    
    public boolean bUpdateCaptureDatastream;
    public boolean bUpdateDcDatastream;
    public boolean bUpdateReslExtDatastream;
    public boolean bUpdateReslIntDatastream;
    public boolean bUpdateManagementDatastream;

    // Default constructor
    public FedoraBean() {
        PID = null;
        type = null;
        oldParent = null;
        dublinCoreData = new DCBean(PID);
        captureData = new CaptureBean(PID);
        original = new OriginalBean(PID);
        content = new ContentBean(PID);
        relations = new RelationsBean(PID);
        thumb = new ThumbBean(PID);
        attachment = new AttachmentBean(PID);
        management = new ManagementBean(PID);
        bUpdateCaptureDatastream = false;
        bUpdateDcDatastream = false;
        bUpdateReslExtDatastream = false;
        bUpdateReslIntDatastream = false;
        bUpdateManagementDatastream = false;
    }
    
    // Constructor
    public FedoraBean(String PID) {
        this.setPID(PID);
        this.setTypeFromPID(PID);
        oldParent = null;
        dublinCoreData = new DCBean(PID);
        captureData = new CaptureBean(PID);
        original = new OriginalBean(PID);
        content = new ContentBean(PID);
        relations = new RelationsBean(PID);
        thumb = new ThumbBean(PID);
        attachment = new AttachmentBean(PID);
        management = new ManagementBean(PID);
        bUpdateCaptureDatastream = false;
        bUpdateDcDatastream = false;
        bUpdateReslExtDatastream = false;
        bUpdateReslIntDatastream = false;
        bUpdateManagementDatastream = false;
    }
    
    // Constructor
    public FedoraBean(String PID, String type) {
        this.setPID(PID);
        this.type = type;
        oldParent = null;
        dublinCoreData = new DCBean(PID);
        captureData = new CaptureBean(PID);
        original = new OriginalBean(PID);
        content = new ContentBean(PID);
        relations = new RelationsBean(PID);
        thumb = new ThumbBean(PID);
        attachment = new AttachmentBean(PID);
        management = new ManagementBean(PID);
        bUpdateCaptureDatastream = false;
        bUpdateDcDatastream = false;
        bUpdateReslExtDatastream = false;
        bUpdateReslIntDatastream = false;
        bUpdateManagementDatastream = false;
    }
    
    /**
     * Get content model
     * @return      name of content model
     */
    public String getContentModel() {
        
        String model = null;
        
        if (this.type != null) {
            model = RepositoryManager.CONTENTMODEL + this.type;
        }
        
        return model;
    }
    
    /**
     * Find datastream according to datastream id
     * @param dataStreamID
     * @return
     */
    public DataStream getDataStream(String dataStreamID) {
        
        if (dataStreamID.equalsIgnoreCase(DATASTREAM_DC)) {
            return this.dublinCoreData;
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_CAPTURE)) {
            return this.captureData;
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_ORIGINAL)) {
            return this.original;
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_RELS_EXT)) {
            return this.relations;
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_THUMB)) {
            return this.thumb;
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_ATTACHMENT)) {
            return this.attachment;
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_MANAGEMENT)) {
            return this.management;
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_CONTENT)) {
            return this.content;
            
        }
        
        return null;
    }
    
    public String getObjectValue(String name) {
        
        String strValue = null;
        
        if (FedoraBean.OBJ_LABEL.equals(name)) {
            strValue = this.objectLabel;
            
        } else if (FedoraBean.OBJ_CONTENTMODEL.equals(name)) {
            strValue = this.objectContentModel;
            
        } else if (FedoraBean.OBJ_CREATIONDATE.equals(name)) {
            strValue = this.objectCreationDate;
            
        } else if (FedoraBean.OBJ_LASTMODIFIED.equals(name)) {
            strValue = this.objectLastModified;
            
        } else if (FedoraBean.OBJ_OWNERID.equals(name)) {
            strValue = this.objectOwnedId;
            
        } else if (FedoraBean.OBJ_STATE.equals(name)){
            strValue = this.eObjectState.toString();
            
        }
        
        return strValue;
    }
    
    /**
     * Set object profile related data
     * @param name      object variable name
     * @param value     object variable value
     */
    public void setObjectData(String name, String value) {
        
        if (FedoraBean.OBJ_LABEL.equals(name)) {
            this.objectLabel = value;
            
        } else if (FedoraBean.OBJ_CONTENTMODEL.equals(name)) {
            this.objectContentModel = value;
            
        } else if (FedoraBean.OBJ_CREATIONDATE.equals(name)) {
            this.objectCreationDate = value;
            
        } else if (FedoraBean.OBJ_LASTMODIFIED.equals(name)) {
            this.objectLastModified = value;
            
        } else if (FedoraBean.OBJ_OWNERID.equals(name)) {
            this.objectOwnedId = value;
            
        } else if (FedoraBean.OBJ_STATE.equals(name)) {
            this.eObjectState = DataStream.State.getEnum(value.toString());
            
        }
    }
    
    public String getPID() { return this.PID; }
    
    public void setPID(String value) { this.PID = value; }
    
    public String getType() { return this.type; }
    
    public void setType(String value) { 
        this.type = value; 
        // if type is general collection
        if (type.equals(RepositoryManager.COLLECTION)) {
            // set exact collection type (base-, group- or unitcollection)
            if (captureData.getMetaDataElements().containsKey(CaptureBean.TYPE)) {
                type = captureData.getMetaDataElements().get(CaptureBean.TYPE).getValue();
            }
        }
    }
    
    public String getOldParent() { return this.oldParent; }
    
    public void setOldParent(String value) { this.oldParent = value; }
       
    /**
     * Convert datastream to XML
     * @param dataStreamID      datastream id
     * @param schemaDocument    schema for object type
     * @return XML datastream as String
     */
    public String getXMLDataStream(String dataStreamID, Document schemaDocument) throws SAXException {

        String OSA_NS = "http://www.mamk.fi";
        String DS_NS = "";
        String ELEMENT_NS = "";
        String retValue = "";
        LinkedHashMap<String, MetaDataElement> elements = null;
        Element rootElement = null;
        String rootElementName = "";
        
        if (dataStreamID.equalsIgnoreCase(DATASTREAM_DC)) {
            
            elements = this.getDataStream(DATASTREAM_DC).getMetaDataElements();
            rootElementName = dataStreamID;
            DS_NS = "http://www.mamk.fi/dc";
            ELEMENT_NS = "dc:";
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_CAPTURE)) {
            
            // sort elements according to schema
            this.sortDataStream(DATASTREAM_CAPTURE, schemaDocument);
            
            elements = this.getDataStream(DATASTREAM_CAPTURE).getMetaDataElements();
            rootElementName = dataStreamID.toLowerCase();
            ELEMENT_NS = "c:";
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_MANAGEMENT)) {
            
            elements = this.getDataStream(DATASTREAM_MANAGEMENT).getMetaDataElements();
            rootElementName = dataStreamID.toLowerCase();
            ELEMENT_NS = "m:";
            
        } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_CONTENT)) {
            
            elements = this.getDataStream(DATASTREAM_CONTENT).getMetaDataElements();
            rootElementName = dataStreamID.toLowerCase();
            ELEMENT_NS = "ft:";
        }
        
        try {
            
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            docFactory.setNamespaceAware(true);
            docFactory.setValidating(true);
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = null;
            
            if (dataStreamID.equalsIgnoreCase(DATASTREAM_DC) 
                || dataStreamID.equalsIgnoreCase(DATASTREAM_RELS_EXT)
                )
            {
                // root element
                doc = docBuilder.newDocument();
                rootElement = doc.createElement(rootElementName);
                doc.appendChild(rootElement);
                
            } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_CAPTURE)
                       || dataStreamID.equalsIgnoreCase(DATASTREAM_MANAGEMENT)
                       || dataStreamID.equalsIgnoreCase(DATASTREAM_CONTENT)
                       ) 
            {
                // root element
                doc = docBuilder.newDocument();
                rootElement = doc.createElement("osa:"+rootElementName);
                doc.appendChild(rootElement);

                // add additional namespaces to the root element
                doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xs", "http://www.w3.org/2001/XMLSchema");
                doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:osa", OSA_NS);
                
                if (dataStreamID.equalsIgnoreCase(DATASTREAM_CAPTURE)) {
                    doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:c", OSA_NS);
                } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_MANAGEMENT)) {
                    doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:m", OSA_NS);
                } else if (dataStreamID.equalsIgnoreCase(DATASTREAM_CONTENT)) {
                    doc.getDocumentElement().setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:ft", OSA_NS);
                }
            }
            
            Iterator<Entry<String, MetaDataElement>> iter = elements.entrySet().iterator();
            while (iter.hasNext()) {
                Map.Entry<String, MetaDataElement> entry = (Map.Entry<String, MetaDataElement>) iter.next();
                MetaDataElement element = entry.getValue();
                
                // Handle multivalues
                // ingest form
                if (element.getMultivalueType().equals(FormElement.MULTIVALUE_DELIMITED) && element.getSource() == null) {
                    String[] multivalues= element.getValue().split(",");
                    for (int i=0; i<multivalues.length; i++) {
                        Element xmlElement = doc.createElementNS(DS_NS, ELEMENT_NS+element.getVisibleName());
                        xmlElement.appendChild(doc.createTextNode(multivalues[i].trim())); // using trim() to remove possible leading and trailing spaces from splitted values
                        doc.getDocumentElement().appendChild(xmlElement);
                    }
                } else if (element.getMultivalueType().equals(FormElement.MULTIVALUE_MULTIFIELD)
                           || !element.getNestedElements().isEmpty()) {
                    // parent element
                    Element parentXmlElement = null;
                    // child elements in hashmap
                    Vector<LinkedHashMap<String, MetaDataElement>> nestedElements = element.getNestedElements();
                    
                    for (int i=0; i<nestedElements.size(); i++) {
                        LinkedHashMap<String, MetaDataElement> lhm = nestedElements.get(i);
                        
                        if (lhm != null) {
                            Iterator<Entry<String, MetaDataElement>> iterLhm = lhm.entrySet().iterator();
                            int index = 0;
                            
                            while (iterLhm.hasNext()) {
                                Map.Entry<String, MetaDataElement> entryLhm = (Map.Entry<String, MetaDataElement>) iterLhm.next();
                                MetaDataElement nestedElement = entryLhm.getValue();
                                
                                if (nestedElement.getName() != null && nestedElement.getValue() != null) {
                                    
                                    if (index == 0) {
                                        // parent element
                                        parentXmlElement = doc.createElementNS(DS_NS, ELEMENT_NS+element.getVisibleName());
                                        // add parent element
                                        doc.getDocumentElement().appendChild(parentXmlElement);
                                    }
                                    // add child elements
                                    Element childElement = doc.createElement(ELEMENT_NS+nestedElement.getName());
                                    childElement.appendChild(doc.createTextNode(nestedElement.getValue()));
                                    parentXmlElement.appendChild(childElement);
                                }
                                index++;
                            }
                        }
                    }
                // move object
                // create element
                } else if (element.getValues().size() > 0) {
                    for (int i=0; i<element.getValues().size(); i++) {
                        if (element.getValues().get(i) != null) {
                            Element xmlElement = doc.createElementNS(DS_NS, ELEMENT_NS+element.getVisibleName());
                            xmlElement.appendChild(doc.createTextNode(element.getValues().get(i)));
                            doc.getDocumentElement().appendChild(xmlElement);
                        }
                    }
                }
            }
                        
            // write the content
            TransformerFactory transformerFactory = TransformerFactory.newInstance();
            Transformer transformer = transformerFactory.newTransformer();
            DOMSource source = new DOMSource(doc);    
            StreamResult result = new StreamResult(new StringWriter());
            transformer.transform(source, result);
            
            retValue = result.getWriter().toString();
            
            // validate capture datastream
            if (dataStreamID.equalsIgnoreCase(DATASTREAM_CAPTURE) && type != null) {
                this.validateDatastream(retValue, schemaDocument);
            }
            
        } catch (ParserConfigurationException e) {
            logger.error("FedoraBean:getXMLDataStream error: "+e);
        } catch (TransformerException e) {
            logger.error("FedoraBean:getXMLDataStream error: "+e);
        } catch (DOMException e) {
            logger.error("FedoraBean:getXMLDataStream error: "+e);
        }
        return retValue;
    }
    
    /**
     * Set type from Pid
     * @param pid
     */
    public void setTypeFromPID(String pid) {
        
        if (pid != null && pid.contains(":")) {
            
            String pidType = pid.split(":")[1].split("-")[0];
            if (RepositoryManager.DATATYPES_MAP.containsKey(pidType)) {
                // get object type from HashMap
                this.type = RepositoryManager.DATATYPES_MAP.get(pidType);
            } else {
                this.type = RepositoryManager.DOCUMENT;
            }
            
            // handle default_types as document
            if (pidType.equalsIgnoreCase("X")) {
                this.type = RepositoryManager.DOCUMENT;
            }
        }
    }
    
    /**
     * Validate datastream according to schema
     * @param strDocument   datastream content
     * @param schemaDoc     schema for object type (document|image|etc.)
     */
    public void validateDatastream(String strDocument, Document schemaDoc) throws SAXException {
        
        try {
            SAXParserFactory spf = SAXParserFactory.newInstance(); 
            spf.setNamespaceAware(true);
            XMLReader reader = spf.newSAXParser().getXMLReader(); 
            
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            XMLSerializer serializer = new XMLSerializer(outputStream, new OutputFormat(schemaDoc));
            serializer.serialize(schemaDoc);
            InputStream is = new ByteArrayInputStream(outputStream.toByteArray());
            SchemaFactory xsFact = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
            Schema schema = xsFact.newSchema(new StreamSource(is));
            
            // Validator
            ValidatorHandler vh = schema.newValidatorHandler(); 
            reader.setContentHandler(vh);
            
            InputSource inputSource = new InputSource(new StringReader(strDocument));
            reader.parse(inputSource);
            
        } catch (IOException e) {
            // instance document is invalid!
            logger.error("FedoraBean:validateDatastream, capture datastream is invalid: "+e);
        } catch (ParserConfigurationException e) {
            // instance document is invalid!
            logger.error("FedoraBean:validateDatastream, capture datastream is invalid: "+e);
        }
    }
    
    public CaptureBean getCaptureData() {
        return captureData;
    }

    public void setCaptureData(CaptureBean captureData) {
        this.captureData = captureData;
    }
    
    public void removeOriginalDatastream() {
        this.original = null;
    }
    
    /**
     * Re-arranges datastream according to the order of elements in schema
     * @param ds                datastream
     * @param schemaDocument    schema for object type (groupcollection|document|image|etc.)
     */
    public void sortDataStream(String ds, Document schemaDocument) {
        
        LinkedHashMap<String, MetaDataElement> elements = this.getDataStream(ds).getMetaDataElements();
        LinkedHashMap<String, MetaDataElement> orderedElements = new LinkedHashMap<String, MetaDataElement>();
        
        XPath xPath = XPathFactory.newInstance().newXPath();
        String expression = "//element[@name='capture']//element";
        NodeList result = null;

        try {
            // Elements in schema
            result= (NodeList) xPath.compile(expression).evaluate(schemaDocument, XPathConstants.NODESET);
            
        } catch (Exception e) {
            logger.error("FedoraBean:sortDataStream error, "+e);
        }
        
        // re-arrange datastream
        for (int i=0; i<result.getLength(); i++) {
            Element e = (Element)result.item(i);
            String key = e.getAttribute("name");
            MetaDataElement element = null;
            element = elements.get(key);
            
            if (element == null) {
                // check if name and visible name differs
                Iterator<Entry<String, MetaDataElement>> iter = elements.entrySet().iterator();
                while (iter.hasNext()) {
                    Map.Entry<String, MetaDataElement> entry = (Map.Entry<String, MetaDataElement>) iter.next();
                    MetaDataElement mdElement = entry.getValue();
                    if (mdElement.getVisibleName() != null && key.equals(mdElement.getVisibleName())) {
                        element = mdElement;
                        break;
                    }
                }                
            }
            
            if (element != null) {
                orderedElements.put(key, element);
            }
        }
        
        // update datastream 
        this.captureData.setMetaDataElements(orderedElements);
    }
    
    /**
     * Extract metadata using Tika
     * @throws Exception
     */
    public static HashMap<String, Object> extractMetadata(File file, String contentType) {

        HashMap<String, Object> captureMetadata = new HashMap<String, Object>();
        String PATTERN_DATE_MODEL = "([0-9]{4}\\-[0-9]{2}\\-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}Z)|([0-9]{4}\\-[0-9]{2}\\-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2})";
        boolean addType = false;
        
        if (contentType == null) {
            String fileExtension = FilenameUtils.getExtension(file.getPath());
            addType = true;
            
            if (fileExtension.equalsIgnoreCase("tif") 
                || fileExtension.equalsIgnoreCase("gif") 
                || fileExtension.equalsIgnoreCase("jpg") 
                || fileExtension.equalsIgnoreCase("jpf")
                || fileExtension.equalsIgnoreCase("jp2")
                || fileExtension.equalsIgnoreCase("jpeg") 
                || fileExtension.equalsIgnoreCase("png")) {
                contentType = RepositoryManager.IMAGE;
                
            } else if (fileExtension.equalsIgnoreCase("wav")
                       || fileExtension.equalsIgnoreCase("wma")) {
                contentType = RepositoryManager.AUDIO;
                
            } else if (fileExtension.equalsIgnoreCase("wmv")
                       || fileExtension.equalsIgnoreCase("m4v")
                       || fileExtension.equalsIgnoreCase("mp4")) {
                contentType = RepositoryManager.MOVINGIMAGE;
                
            } else {
                contentType = RepositoryManager.DOCUMENT;
            }
        }
        
        try {
            InputStream input = new FileInputStream(file);
            ContentHandler textHandler = new BodyContentHandler();
            Metadata metadata = new Metadata();
            Parser parser = new AutoDetectParser();
            ParseContext context = new ParseContext();
            
            parser.parse(input, textHandler, metadata, context);
            input.close();
            
            HashMap<String, String> mappings = getMappings(contentType);
            
            // metadata found by Tika
            for (String key : metadata.names()) {
                String value = metadata.get(key);
                // check if found to be mapped with capture's metadata
                if (mappings.containsKey(key)) {
                    // check date format
                    if (value.matches(PATTERN_DATE_MODEL)) {
                        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
                        Object o = simpleDateFormat.parse(value);
                        value = simpleDateFormat.format((Date)o);
                    }
                    
                    captureMetadata.put(mappings.get(key), value);
                }
            }
            
            // add file information
            if (file.isFile()) {
                // add file size
                double fileSize = (double) (file.length()/1024.0);
                String unit = " kB";
                
                if (fileSize > 1000) {
                    fileSize = fileSize/1024.0;
                    unit = " MB";
                }
                
                String strFileSize = String.format("%.1f", fileSize).concat(unit);               
                captureMetadata.put("extentsize", strFileSize);
                
                // add file name
                captureMetadata.put("fileName", file.getName());
                
            }            
            
            if (addType) {
                captureMetadata.put(CaptureBean.TYPE, contentType);
            }
            
        } catch (Exception e) {
            logger.error(e);
        }
        
        return captureMetadata;
    }
    
    /**
     * Extract content using Tika
     * @throws Exception
     */
    public static String extractContent(File file) {
        String content = null;
        if (file == null) {
            return content;
        }
        
        try {
            InputStream input = new FileInputStream(file);
            ContentHandler textHandler = new BodyContentHandler();
            Metadata metadata = new Metadata();
            Parser parser = new AutoDetectParser();
            ParseContext context = new ParseContext();
            
            parser.parse(input, textHandler, metadata, context);
            input.close();
            
            content = textHandler.toString();
            // remove spaces
            content = content.replaceAll("^\\s*", " ");
            
        } catch (Exception e) {
            logger.error(e);
        }
        
        return content;
    }
    
    /**
     * Get tika/capture parameter mappings
     * @param contenttype
     * @return
     * @throws SAXException
     * @throws IOException
     * @throws ParserConfigurationException
     */
    public static HashMap<String, String> getMappings(String contentType) throws SAXException, IOException, ParserConfigurationException {
        
        HashMap<String, String> xmlMappings = new HashMap<String,String>();
        File mappingFile = new File(Osa.configFolder+"tika_mappings.xml");
        DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
        
        DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
        Document doc = dBuilder.parse(mappingFile);
        doc.getDocumentElement().normalize();
        NodeList nodes = doc.getElementsByTagName("content");
        for (int i=0; i<nodes.getLength();i++) {
            Node node = nodes.item(i);
            if (node.getNodeType() == Node.ELEMENT_NODE) {
                if (node.getAttributes().getNamedItem("type").getNodeValue().equals(contentType)) {
                    NodeList subNodes = node.getChildNodes();
                    for (int j = 0;j < subNodes.getLength();j++) {
                        Node subNode = subNodes.item(j);
                        if (subNode.getNodeType() == Node.ELEMENT_NODE) {
                            String osaVal = subNode.getAttributes().getNamedItem("osa").getNodeValue();
                            String tikaVal = subNode.getAttributes().getNamedItem("tika").getNodeValue();
                            xmlMappings.put(tikaVal, osaVal);
                        }
                    }
                }
                
            }
        }
        return xmlMappings;
    }
    
} public class MetaDataElement implements Serializable {

    private static final long serialVersionUID = -5496828257828486275L;
    private static final Logger logger = Logger.getLogger(MetaDataElement.class);
    
    // indicates the kind of metadata
    public static enum MetaDataType {
        string,     // String
        date,       // Date
        integer,    // Integer
        link,       //
        relation,   // Object relationships
        select;     // Enum value
        
        public static MetaDataType getEnum(String s) {
            if (string.name().equalsIgnoreCase(s))
                return string;
            else if (date.name().equalsIgnoreCase(s))
                return date;
            else if (integer.name().equalsIgnoreCase(s))
                return integer;
            else if (link.name().equalsIgnoreCase(s))
                return link;
            else if (relation.name().equalsIgnoreCase(s))
                return relation;
            else if (select.name().equalsIgnoreCase(s))
                return select;
            else if (s.startsWith("text") || s.startsWith("time"))
                return string;
            throw new IllegalArgumentException("No Enum specified for this string");
        }
    }
    
    private String name;
    private String visibleName;
    private String value;
    private Vector<String> values;
    private String visibleValue;
    private MetaDataType eMetaDataType;
    // Multivalue types: FormElement.MULTIVALUE_DELIMITED / FormElement.MULTIVALUE_MULTIFIELD / FormElement.MULTIVALUE_NONE
    private String multivalueType;
    // Name of the ontology, if used in this field
    private String source;
    // Is value automatically generated
    private boolean generated;
    // For original, attachment, preview content
    private InputStream inputStream;

    // list of datastreams, where metadata belongs to
    // used, when mapping metadata to datastreams determined in contentmodel
    private List<String> datastreams;
    String datastream;
    
    // Enumerated values for field
    private List<String> enumValues;

    private Vector<LinkedHashMap<String, MetaDataElement>> nestedElements;

    // Default constructor
    public MetaDataElement() {
        this.name = null;
        this.visibleName = null;
        this.value = null;
        this.values = new Vector<String>();
        this.generated = false;
        this.eMetaDataType = MetaDataType.string;
        this.multivalueType = FormElement.MULTIVALUE_NONE;
        this.source = null;
        this.inputStream = null;       
        this.nestedElements = new Vector<LinkedHashMap<String, MetaDataElement>>();
        this.datastream = null;
        this.datastreams = new ArrayList<String>();
    }
    
    // Constructor
    public MetaDataElement(String name, String visibleName, String value, MetaDataType eType) {
        this.name = name;
        this.visibleName = visibleName;
        this.value = value;
        this.values = new Vector<String>();
        this.values.add(value);
        
        if (isRelation(name)) {
            this.eMetaDataType = MetaDataType.relation;
        } else {
            this.eMetaDataType = eType;
        }
        
        this.generated = false;
        this.multivalueType = FormElement.MULTIVALUE_NONE;
        this.source = null;
        this.inputStream = null;
        this.nestedElements = new Vector<LinkedHashMap<String, MetaDataElement>>();
        this.datastream = null;
        this.datastreams = new ArrayList<String>();
    }
    
    // Constructor
    public MetaDataElement(MetaDataElement element) {
        this.name = element.getName();
        this.visibleName = element.getVisibleName();
        this.value = element.getValue();
        this.values = new Vector<String>();
        this.values = element.getValues();
        this.generated = element.getGenerated();
        this.eMetaDataType = element.getMetaDataType();
        this.multivalueType = element.getMultivalueType();
        this.source = element.getSource();
        this.inputStream = element.getInputStream();
        this.nestedElements = new Vector<LinkedHashMap<String, MetaDataElement>>();
        this.nestedElements = element.getNestedElements(); 
        this.datastream = element.getDatastream();
        this.datastreams = element.getDatastreams();
    }
    
    // Constructor
    public MetaDataElement(String name, 
                           String visibleName, 
                           String value, 
                           MetaDataType eType, 
                           String multivalue, 
                           InputStream is,
                           Vector<LinkedHashMap<String, MetaDataElement>> nestedElements,
                           String ds) {
        this.name = name;
        this.visibleName = visibleName;
        this.value = value;
        this.values = new Vector<String>();
        this.values.add(value);
        
        if (isRelation(name)) {
            this.eMetaDataType = MetaDataType.relation;
        } else {
            this.eMetaDataType = eType;
        }
        
        this.generated = false;
        this.multivalueType = multivalue;
        this.source = null;
        this.inputStream = is;
        this.nestedElements = new Vector<LinkedHashMap<String, MetaDataElement>>();
        if (nestedElements != null) {
            this.nestedElements = nestedElements;
        }
        this.datastream = ds;
        this.datastreams = new ArrayList<String>();
        this.setDatastream(ds);
    }
    
    @Override
    public boolean equals(Object object) {
        boolean isEqual = false;       

        if (object != null && object instanceof MetaDataElement) {
            String objectName = ((MetaDataElement) object).getName();
            isEqual = (this.name.equalsIgnoreCase(objectName));
        }

        return isEqual;
    }

    /**
     * Check if metadata element has value
     * @return
     */
    public boolean isEmpty() {
        boolean retValue = true;
        if (this.getValue() != null) {
            retValue = false;
        } else if (this.getValues().size() > 0 && this.getValues().get(0) != null) {
            retValue = false;
        } else if (this.getNestedElements().size() > 0) {
            Vector<LinkedHashMap<String, MetaDataElement>> nestedElements = this.getNestedElements();
            for (LinkedHashMap<String, MetaDataElement> lhm : nestedElements) {
                if (lhm != null) {
                    Iterator<Entry<String, MetaDataElement>> iterLhm = lhm.entrySet().iterator();
                    while (iterLhm.hasNext()) {
                        Map.Entry<String, MetaDataElement> entryLhm = (Map.Entry<String, MetaDataElement>) iterLhm.next();
                        MetaDataElement nestedElement = entryLhm.getValue();
                        if (nestedElement.getValue() != null) {
                            retValue = false;
                        } else if (nestedElement.getValues().size() > 0 && nestedElement.getValues().get(0) != null) {
                            retValue = false;
                        }
                        break;
                    }
                }
            }
        }
        return retValue;
    }
    
    @Override
    public String toString() {
        return "metadataelement [name="+name+", value="+value+", values="+values.toString()+", type="+eMetaDataType.toString()+", nestedElements="+nestedElements+"]";
    }
    
    public List<String> getEnumValues() { return this.enumValues; }
    
    public void setEnumValues(List<String> value) {
        if (value != null) {
            this.enumValues = value;
        }
    }
    
    public MetaDataType getMetaDataType() { return this.eMetaDataType; }
    
    public void setMetaDataType(MetaDataType value) { this.eMetaDataType = value; }
    
    public String getName() { return this.name; }
    
    public void setName(String value) {
        this.name = value;
        if (isRelation(this.name)) {
            this.eMetaDataType = MetaDataType.relation;
        }
    }
    
    public String getVisibleName() { return this.visibleName; }
    
    public void setVisibleName(String value) {
        this.visibleName = value;
        if (isRelation(this.visibleName)) {
            this.eMetaDataType = MetaDataType.relation;
        }
    }
    
    public List<String> getDatastreams() { return this.datastreams; }
    
    public String getDatastream() { return this.datastream; }
    
    public void setDatastream(String value) {
        this.datastream = value;
        if (value != null || value != "") {
            this.datastreams.add(value);
        }
    }
    
    public String getValue() {
        return this.value;
    }
    
    public void removeValue() { 
        this.value = "";
        this.values.removeAllElements();
    }
    
    public void setValue(String value) { 
        this.value = value;
        if (!this.values.contains(value)) {
            this.values.add(value);
        }
    }
    
    public Vector<String> getValues() { 
        return this.values;
    }
    
    public void setValues(Vector<String> values) { 
        this.values = values;
    }
    
    public String getVisibleValue() {
        return visibleValue;
    }

    public void setVisibleValue(String visibleValue) {
        this.visibleValue = visibleValue;
    }
    
    public String getMultivalueType() {
        return multivalueType;
    }

    public void setMultivalueType(String value) {
        this.multivalueType = value;
    }

    public String getSource() {
        return this.source;
    }
    
    public void setSource(String value) {
        this.source = value;
    }
    
    public InputStream getInputStream() {
        return inputStream;
    }

    public void setInputStream(InputStream value) {
        this.inputStream = value;
    }
    
    public boolean getGenerated() {
        return this.generated;
    }
    
    public void setGenerated(boolean value) {
        this.generated = value;
    }
    
    public boolean isRelation(String name) {
        boolean retValue = false;
        
        if (this.name.equalsIgnoreCase(CaptureBean.RELATION_ConformsTo)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_HasFormat)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_HasPart)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_HasRedaction)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_HasVersion)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsChildOf)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsFormatOf)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsMemberOf)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsParentOf)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsPartOf)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsRedactionOf)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsReferencedBy)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsReplacedBy)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsRequiredBy)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_IsVersionOf)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_References)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_Replaces)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_Requires)
            || this.name.equalsIgnoreCase(CaptureBean.RELATION_Source)
            )
        {
            retValue = true;
        }
        
        return retValue;
    }

    public Vector<LinkedHashMap<String, MetaDataElement>> getNestedElements() {
        return nestedElements;
    }

    public void setNestedElements(Vector<LinkedHashMap<String, MetaDataElement>> nestedElements) {
        this.nestedElements = nestedElements;
    }
    
    public void setNestedElement(String name, MetaDataElement nestedElement) {

        boolean addNewHashMap = false;
        if (nestedElements.isEmpty()) {
            LinkedHashMap<String, MetaDataElement> lhm = new LinkedHashMap<String, MetaDataElement>();
            lhm.put(name, nestedElement);
            this.nestedElements.add(lhm);
            
        } else {
            
            for (int i=0; i<this.nestedElements.size(); i++) {
                LinkedHashMap<String, MetaDataElement> currentLhm = this.nestedElements.get(i);
                if (currentLhm.containsKey(name)) {
                    addNewHashMap = true;
                } else {
                    this.nestedElements.get(i).put(name, nestedElement);
                    addNewHashMap = false;
                }
            }
            
            if (addNewHashMap) {
                LinkedHashMap<String, MetaDataElement> lhm = new LinkedHashMap<String, MetaDataElement>();
                lhm.put(name, nestedElement);
                this.nestedElements.add(lhm);
                addNewHashMap = false;
            }
        }
    }

} public static enum State {
    DEFAULT,
    A,          // Active
    I,          // Inactive
    D;          // Deleted
        
    public static State getEnum(String s) {
        if (A.name().equals(s))
            return A;
        else if (I.name().equals(s))
            return I;
        else if (D.name().equals(s))
            return D;
        throw new IllegalArgumentException("No Enum specified for this string");
    }
} public class DestructionPlan {
    
    private static final Logger logger = Logger.getLogger(DestructionPlan.class);
    
    public static final String DESTRUCTIONTYPE_Dispose = "dispose";
    public static final String DESTRUCTIONTYPE_Delete = "delete";
    public static final String DESTRUCTIONREASON_Scheduled = "scheduled";
    
    private String approvalPolicy = "";
    private String disposeMethod = "";
    private boolean allowOrphans = false;
    private boolean createResidualObjects = false;
    private boolean recursiveDestruction = false; // TODO: check if needed
    private boolean scheluded = false;
    private List<String> residualDatastreams = new ArrayList<String>();
    private List<String> residualMetadata = new ArrayList<String>();
    
    public DestructionPlan(File configuration) {
        
        try {
        	
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(configuration);
            doc.getDocumentElement().normalize();
            
        	NodeList disposeSettings = doc.getElementsByTagName("destruction");
            
            for (int i=0; i<disposeSettings.getLength(); i++) {

                Node item = disposeSettings.item(i);
            	if (item.getNodeType() == Node.ELEMENT_NODE)	{

            	    NodeList childNodes = item.getChildNodes();
            		for (int j=0; j<childNodes.getLength(); j++) {
            			Node subItem = childNodes.item(j);
            			if (subItem.getNodeType() == Node.ELEMENT_NODE) {
            				if (subItem.getNodeName().equalsIgnoreCase("method")) {
            					setDisposeMethod(subItem.getTextContent());
            				}
            				
            				if (subItem.getNodeName().equalsIgnoreCase("approval")) {
                                setApprovalPolicy(subItem.getTextContent());
                            }
            				
            				if (subItem.getNodeName().equalsIgnoreCase("scheduled")) {
            				    setScheluded(Boolean.parseBoolean(subItem.getTextContent()));
                            }
            				// TODO: check if needed
            				if (subItem.getNodeName().equalsIgnoreCase("recursiveDestruction")) {
            					setRecursiveDestruction(Boolean.parseBoolean(subItem.getTextContent()));
            				}
            				
            				if (subItem.getNodeName().equalsIgnoreCase("allowOrphans")) {
            				    setAllowOrphans(Boolean.parseBoolean(subItem.getTextContent()));
            				}
            				
            				if (subItem.getNodeName().equalsIgnoreCase("createResidualObjects")) {
                                setCreateResidualObjects(Boolean.parseBoolean(subItem.getTextContent()));
                            }
            				
            				if (subItem.getNodeName().equalsIgnoreCase("residualObjectDefinition")) {
            				    
            				    NodeList residualObject_metadata = doc.getElementsByTagName("metadata").item(0).getChildNodes();
            				    // get metadata to be preserved
                                for (int x=0; x<residualObject_metadata.getLength(); x++) {
                                    Node metadata = residualObject_metadata.item(x);
                                    if (metadata.getNodeType() == Node.ELEMENT_NODE) {
                                        String mdName = metadata.getAttributes().getNamedItem("name").getNodeValue();
                                        residualMetadata.add(mdName);
                                    }
                                }
            				    
            				    NodeList residualObject_datastreams = doc.getElementsByTagName("dataStreams").item(0).getChildNodes();
            				    // get datastream names to be preserved
                                for (int x=0; x<residualObject_datastreams.getLength(); x++) {
                                    Node datastream = residualObject_datastreams.item(x);
                                    if (datastream.getNodeType() == Node.ELEMENT_NODE) {
                                        String dsName = datastream.getAttributes().getNamedItem("id").getNodeValue();
                                        residualDatastreams.add(dsName);
                                    }
                                }
                            }

            			}
            		}			
            	}	
            }
    		
        } catch(Exception e) {
        	logger.error(e);
        }
        // TODO Load the disposal plan
    }
    
    public String getApprovalPolicy() {
        return approvalPolicy;
    }
    
    public void setApprovalPolicy(String approvalPolicy) {
        this.approvalPolicy = approvalPolicy;
    }
    
    public void setCreateResidualObjects(boolean createResidualObjects) {
        this.createResidualObjects = createResidualObjects;
    }
    
    public boolean isCreateResidualObjects() {
        return createResidualObjects;
    }
    
    public String getDisposeMethod() {
    	return disposeMethod;
    }
    
    public void setDisposeMethod(String disposeMethod) {
    	this.disposeMethod = disposeMethod;
    }
    
    public boolean isRecursiveDestruction() {
    	return recursiveDestruction;
    }
    
    public void setRecursiveDestruction(boolean recursiveDestruction) {
    	this.recursiveDestruction = recursiveDestruction;
    }
    
    public boolean isAllowOrphans() {
        return allowOrphans;
    }
    
    public void setAllowOrphans(boolean allowOrphans) {
        this.allowOrphans = allowOrphans;
    }
    
    public void setScheluded(boolean scheluded) {
        this.scheluded = scheluded;
    }
    
    public boolean isScheluded() {
        return scheluded;
    }
    
    public List<String> getResidualMetadata() {
        return residualMetadata;
    }
	
    public List<String> getResidualDatastreams() {
        return residualDatastreams;
    }
        
} public class Osa extends GenericServlet
{
    private static final long serialVersionUID = 1L;
    private static final Logger logger = Logger.getLogger(Osa.class);

    public static AuthenticationManager authManager;

    public static HashMap<String, DatabaseManager> dbManager = new HashMap<String, DatabaseManager>();
    public static SearchManager searchManager;
    public static RepositoryManager fedoraManager;
    public static WorkflowManager workflowManager;
    public static TaskScheduler taskScheduler;
    
    public static String persistence;
    public static String systemDatabase;
    public static String documentDatabase;
    public static String authentication;
    public static String search;
    public static String logging;
    public static String defaultConfiguration;
    public static String defaultRoot;
    public static String configFolder;
    public static String onkiAccessKey;
    
    public static String tempDirectory;
    public static String dataDirectory;
    public static String logDirectory;
    public static String importDirectory; 
    public static String ingestDirectory;
    public static String failedDirectory;
    public static String uploadDirectory;
    
    // TODO: move to database manager class
    public static DatabaseConfiguration dbConfiguration;
    
    // TODO: move to solr manager class
    public static String solrServerUrl;
	
    /** 
     * 	Initializes Osa instance
     */
    public void init() throws ServletException {
        
        Properties properties = new Properties();
        try {
            
            properties.load(new FileInputStream(getServletContext().getRealPath("/") + this.getInitParameter("config")));
            
            // Directories
            // TODO check if directories exist and are accessible
            tempDirectory = properties.getProperty("osa.directory.temp");
            dataDirectory = properties.getProperty("osa.directory.data");
            logDirectory = properties.getProperty("osa.directory.log");
            importDirectory = properties.getProperty("osa.directory.import"); 
            ingestDirectory = properties.getProperty("osa.directory.ingest");
            failedDirectory = properties.getProperty("osa.directory.failed");
            uploadDirectory = properties.getProperty("osa.directory.upload");            
                        
            // Persistence
            persistence = properties.getProperty("osa.persistence");
            
            switch (persistence) {
        	case RepositoryManager.FEDORA3:
        	    String host = properties.getProperty("osa.fedora.server");
        	    String port = properties.getProperty("osa.fedora.port");
        	    String username = properties.getProperty("osa.fedora.username");
        	    String password = properties.getProperty("osa.fedora.password");
        	    String schemaPath =  getServletContext().getRealPath("/") + "WEB-INF/";
        	    
        		Osa.fedoraManager = new Fedora3Manager(host, port, username, password, schemaPath);
        		
        		// TODO: Check Fedora server status
        		break;
        		
        	case RepositoryManager.FEDORA4:
        	    // TODO: version 4 support
        	    break;
        
        	default:
        		throw new Exception("OSA instance requires a persistence module.");
            }                
            
            // Database
            systemDatabase = properties.getProperty("osa.database.system");
            
            // TODO: Add support for multiple coexisting databases.  
            switch (systemDatabase) {
                case "mysql":
                case "mariadb":
	                Osa.dbConfiguration = new DatabaseConfiguration();			
	                Osa.dbConfiguration.setDbURI(properties.getProperty("osa.mysql.connectionString"));
	                Osa.dbConfiguration.setDbDriverName(properties.getProperty("osa.mysql.driver"));
	                Osa.dbConfiguration.setDbUser(properties.getProperty("osa.mysql.user"));
	                Osa.dbConfiguration.setDbPassword(properties.getProperty("osa.mysql.password"));
	                Osa.dbConfiguration.setDbPoolMinSize(Integer.parseInt(properties.getProperty("osa.mysql.poolMinSize".trim())));
	                Osa.dbConfiguration.setDbPoolMaxSize(Integer.parseInt(properties.getProperty("osa.mysql.poolMaxSize").trim()));
	                Osa.dbConfiguration.setDbTimeout( Integer.parseInt(properties.getProperty("osa.mysql.timeout").trim()) );
	                
	                new ConnectionManager(dbConfiguration);
	                Osa.dbManager.put("sql", new MySQLManager());
	                //logger.info("Database configuration loaded.");	                
					break;
	
				default:
					throw new Exception("OSA instance requires a system database module.");
			}
            
            documentDatabase = properties.getProperty("osa.database.document");
            
            switch (documentDatabase) {
				case "mongodb":
					String mongohost = properties.getProperty("osa.mongo.host");
					int mongoport = Integer.parseInt(properties.getProperty("osa.mongo.port"));
					String mongoDefaultdb = properties.getProperty("osa.mongo.defaultdb");
					String mongoDefautCollection = properties.getProperty("osa.mongo.defaultcollection");
					
	                Osa.dbManager.put("mongo", new MongoManager(mongohost, mongoport, mongoDefaultdb, mongoDefautCollection));
	                //logger.info("Mongo configuration loaded.");	                
					break;
	
				default:
					throw new Exception("OSA instance requires a document database module.");
			}
            
            
            // Authentication
            authentication = properties.getProperty("osa.authentication");
            switch (authentication) {
            	case "ldap":
                    Osa.authManager = new LdapManager(
                            properties.getProperty("osa.ldap.server"),
                            properties.getProperty("osa.ldap.admindn"),
                            properties.getProperty("osa.ldap.managepw"),
                            properties.getProperty("osa.ldap.basedn"),
                            properties.getProperty("osa.ldap.instanceadmin")
                            );
                    
                    // TODO: Check LDAP server status
            		break;
            
            	default:
            		throw new Exception("OSA instance requires an authentication module.");
            }
          
            
            // Search
            search = properties.getProperty("osa.search");
            switch (search) {
				case "solr":
					
					// TODO Refactor inside the manager implementation
	            	Osa.solrServerUrl = properties.getProperty("osa.solr.server");

	            	Osa.searchManager = new SolrManager();
	            	
					// TODO test sorl server status
					break;

				default:
					throw new Exception("OSA instance requires a search module.");
			}

            
            // Logger
            logging = properties.getProperty("osa.logger");
            switch (logging) {
            	case "log4j":
                    String configurationFile = this.getInitParameter("logger");
                    File file = new File(getServletContext().getRealPath("/") + configurationFile);
                    if(!file.canRead()) 
                    {
                        throw new Exception("Couldn't read logger configuration file at " + file.getAbsolutePath());
                    }
                    
                    PropertyConfigurator.configure(file.getAbsolutePath()); 
            		break;
            
            	default:
            		throw new Exception("OSA instance requires a logger module.");
            }            
            
            
            // Workflows
            String engineHost = properties.getProperty("osa.workflowengine.host");
            String enginePort = properties.getProperty("osa.workflowengine.port");
            try {
                Osa.workflowManager = new WorkflowManager(engineHost, enginePort);
            } catch (Exception e) {
                logger.info("WorkflowManager not available: "+e);
            }
           
            // Load default conf.
            Osa.defaultConfiguration = properties.getProperty("osa.default.configuration");
            Osa.configFolder = getServletContext().getRealPath("/") + "WEB-INF/config/";
            Osa.defaultRoot = properties.getProperty("osa.default.root");
            
            
            // ONKI access key according to external IP address
            final String prefix = "osa.onki.key.";
            String externalIP = getIP();
            char[] chArr = prefix.toCharArray();  
            chArr[chArr.length -1]++;  
            String endPrefix = new String(chArr);
            Set<String> allNames = properties.stringPropertyNames();  
            SortedSet<String> names = new TreeSet<String>(allNames).subSet(prefix, endPrefix);
            Iterator<String> it = names.iterator();  
            while (it.hasNext()) {
                String key = it.next();
                if (key.endsWith(externalIP)) {
                    Osa.onkiAccessKey = properties.getProperty(key);
                    break;
                }
            }
            
            if (Osa.onkiAccessKey == null) {
                // Default value
                Osa.onkiAccessKey = properties.getProperty("osa.onki.key.default");
            }
            
            // Scheduled tasks
            if (properties.getProperty("osa.scheduledtasks").equals("1")) {
                String configPath =  getServletContext().getRealPath("/") + "WEB-INF/";
                taskScheduler = new TaskScheduler("AutomaticDestruction", "triggername", logDirectory, configPath);
                taskScheduler.start();
            }
            
            logger.info("OSA version [" + getVersionString() + "] + started. See log for more information.");

        } catch (Exception e) {
            logger.error("Error initializing OSA instance. " + e);
            System.exit(0);
        }
    }
    
    public void service(ServletRequest arg0, ServletResponse arg1) throws ServletException, IOException {
    	throw new UnsupportedOperationException("Not supported.");	
    }
    
    public static String getVersionString() {
    	return "release 1.0 - december 2014";
    }
    
    /**
     * Get external IP address
     */
    public String getIP() throws IOException {
        String ip = "";
        URL whatismyip = new URL("http://checkip.amazonaws.com");
        BufferedReader in = new BufferedReader(new InputStreamReader(whatismyip.openStream()));
        ip = in.readLine();
        return ip;
    }

} public class MongoManager extends DatabaseManager {
    
    private static final Logger logger = Logger.getLogger(MongoManager.class);
	public static String EVENT_REMOVE = "remove";
	public static String EVENT_RENAME = "rename";
	public static String EVENT_ADD 	  = "add";
	public static String EVENT_UPDATE = "update";
	
	// Collections names
	private static final String DB_BASKET	   = "basket";
	private static final String DB_DISPOSAL	   = "disposal";
	private static final String DB_LATESTEDITS = "latestEdits";
	private static final String DB_METADATA	   = "metadatafiles";
	
	// Names of arrays holding the documents in collections
	private static final String ARRAY_BASKET	  = "pids";
	private static final String ARRAY_DISPOSAL	  = "disposalItems";
	private static final String ARRAY_LATESTEDITS = "pids";
	private static final String ARRAY_METADATA	  = "metadatafiles";
	
	//Only for testing purposes, for final implementation these should be specified in conf.
	private String Host;
	private int port;
	private String defaultDB;
	private String defaultCollection;
	
	private MongoClient mongo;
	private DB db;
	private DBCollection table;
	
	public MongoManager(String host, int port, String dbname, String collection) {
		this.Host = host;
		this.port = port;
		this.defaultDB = dbname;
		this.defaultCollection = collection;
		try {
			this.mongo = new MongoClient(this.Host,this.port);
			this.db = mongo.getDB(this.defaultDB);
			this.table = db.getCollection(this.defaultCollection);
			
		} catch (UnknownHostException e) {
		    logger.error("MongoManager initializing error, "+e);
		}
		
	}
	
	public void close() {
	    this.mongo.close();
	}
	
	@Override
	public boolean testConnection() {
		// TODO Auto-generated method stub
		return super.testConnection();
	}
	
	public void selectDB(String db) {
		this.db = mongo.getDB(db);	
	}
	
	public void selectCollection(String collection) {
		this.table = db.getCollection(collection);
	}
	
	public void insertObject(String json) {
		this.selectCollection(defaultCollection);
		DBObject dbocject = (DBObject) JSON.parse(json);
		this.table.insert(dbocject);
	}
	
	public void removeObject(String objectid) {
		this.selectCollection(defaultCollection);
	    BasicDBObject query = new BasicDBObject();
	    query.put("_id", new ObjectId(objectid));
	    table.remove(query);
	}
	
	public String getFeedbackSize(){
		this.selectCollection(defaultCollection);	
		long feedbackSize = table.count();		
		return String.valueOf(feedbackSize+1);
	}
	
	public String listItems() {
		
		this.selectCollection(defaultCollection);
		DBCursor cursor = table.find();
		String jsonResult = "[";	
		while (cursor.hasNext()) {
			jsonResult += cursor.next().toString() +",";
		}

		cursor.close();		
		jsonResult += "]";			
		return jsonResult;
	}
	
	public String insertToBasket(User user, String pid) {
		this.selectCollection(DB_BASKET);
		
		String success = "";
		String username = user.getOrganization().getName()+":"+user.getMail();
		String objName = Osa.searchManager.getItemByPid(pid, user, user.getLocale()).replace("[", "").replace("]", "");
		String objType = Osa.searchManager.getObjectType(pid, user);
		
		Vector<HashMap<String, String>> pidVector = new Vector<HashMap<String, String>>();
		HashMap<String, String> pidDatamap = new HashMap<String, String>();
		
		BasicDBObject query = new BasicDBObject();
		DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_BASKET,1).append("_id",false));
		// If basket already has items
		if (cursor.size() != 0) {
			success = this.updateBasket(user, pid, objName);
		} else {
			// If basket is empty, create new
			pidDatamap.put("pid", pid);
			pidDatamap.put("name", objName);
			pidDatamap.put("type", objType);
			pidVector.add(pidDatamap);
			
			query.put("username", username);
			query.put(ARRAY_BASKET, pidVector);
			
			this.table.insert(query);
			success = "success";
		}
		cursor.close();
		return success;
	}
	
	public String updateBasket(User user, String newpid, String objname) {
		this.selectCollection(DB_BASKET);
		String username = user.getOrganization().getName()+":"+user.getMail();
		String success = "";
		String objType = Osa.searchManager.getObjectType(newpid, user);
		
		if (!checkIfExists(user, newpid)) {
			BasicDBObject updateQuery = new BasicDBObject("username", username);
			BasicDBObject updateCommand = new BasicDBObject();
			
			updateCommand.put("$push", 
							new BasicDBObject(ARRAY_BASKET, new BasicDBObject("pid",newpid)
							.append("name", objname)
							.append("type", objType)));
			this.table.update(updateQuery, updateCommand);
			success = "success";
		} else {
			success = "alreadyexists";
		}
		return success;
	}
	
	/**
     * @param user      name of the user (cn from ldap)
     * @param pid       pid of the object to be deleted
     */
    public void deleteFromBasket(User user, String pid) {
        this.selectCollection(DB_BASKET);
        String username = user.getOrganization().getName()+":"+user.getMail();
        // Delete from this user
        BasicDBObject updateQuery = new BasicDBObject("username", username);
        // from pids array where pid = given pid
        BasicDBObject updateCommand = new BasicDBObject(ARRAY_BASKET, new BasicDBObject("pid", pid));
        this.table.update(updateQuery, new BasicDBObject("$pull", updateCommand));
    }
    
    public void syncBaskets(String pid, String event, String newName) {
        this.selectCollection(DB_BASKET);
        BasicDBObject updateQuery = new BasicDBObject();
        // Deletes all entries with given pid. The second (multi) true flag enables multi update
        if (event.equals(EVENT_REMOVE)) {
	        BasicDBObject updateCommand = new BasicDBObject(ARRAY_BASKET, new BasicDBObject("pid", pid));
	        this.table.update(updateQuery, new BasicDBObject("$pull", updateCommand), false, true);
        }
        else if (event.equals(EVENT_RENAME)) {
        	updateQuery = new BasicDBObject("pids.pid", pid);
        	BasicDBObject updateCommand = new BasicDBObject("pids.$.name", newName);
	        this.table.update(updateQuery, new BasicDBObject("$set", updateCommand), false, true);
        }
    }
    
	/**
	 * @param user		User object
	 * @param pid		pid of the object
	 * @return true or false
	 */
	public boolean checkIfExists(User user, String pid) {
		this.selectCollection(DB_BASKET);
		String username = user.getOrganization().getName()+":"+user.getMail();
		boolean exists = false;		
		DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_BASKET,1).append("_id",false));
		
		while (cursor.hasNext()) {
			if (cursor.next().get(ARRAY_BASKET).toString().contains(pid)) {
				exists = true;
			}
		}
		cursor.close();
		return exists;
	}
	
	public List<BasketItem> getBasketItems(User user) {
		this.selectCollection(DB_BASKET);
		String username = user.getOrganization().getName()+":"+user.getMail();
		DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_BASKET,1).append("_id",false));
		List<BasketItem> basketItems = new ArrayList<BasketItem>();
		
		if (cursor.size() > 0) {
			while (cursor.hasNext()) {
				List<Map<String,String>> result = new JSONDeserializer<List<Map<String,String>>>().deserialize(cursor.next().get(ARRAY_BASKET).toString());
		        for (int i = 0; i < result.size(); i++) {
			        BasketItem basketItem = new BasketItem();
			        basketItem.setPid(result.get(i).get("pid"));
			        basketItem.setType(result.get(i).get("type"));
			        basketItem.setName(result.get(i).get("name"));
			        basketItems.add(basketItem);
		        }
			}
		}
		cursor.close();
		return basketItems;
	}
	
	/**
	 * @param user		name of the user (cn from ldap)
	 * 
	 * @return Content of the basket in json format
	 */
	public String listBasketContent(User user) {
		this.selectCollection(DB_BASKET);
		String username = user.getOrganization().getName()+":"+user.getMail();
		String jsonresult = "";
		// Exclude database row id from results with .append("_id",false)
		DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_BASKET,1).append("_id",false));
    	
		if (cursor.size() > 0) {
			while (cursor.hasNext()) {
				jsonresult = "["+cursor.next().toString();
			}
		}

		if (cursor.size() > 0) {
			jsonresult += "]";
		}
		cursor.close();
		return jsonresult;
	}
	
	/** Counts the amount of objects in given user's basket.
	 * 
	 * @param	user	Username, ldap cn
	 * @return	Integer number of items in basket
	 */
	public int getBasketSize(User user) {
	    this.selectCollection(DB_BASKET);
	    String username = user.getOrganization().getName()+":"+user.getMail();
	    DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_BASKET,1).append("_id",false));
	    int size = 0;
	    
	    while (cursor.hasNext()) {
	        Map<String,List<Map<String,String>>> result = new JSONDeserializer<Map<String,List<Map<String,String>>>>().deserialize(cursor.next().toString());
	        List<Map<String,String>> map = result.get(ARRAY_BASKET);
	        size += map.size();
	    }
	    cursor.close();
		return size;
	}
	
	public void clearTable(String tablename) {
		this.selectCollection(tablename);
		this.table.drop();
		logger.info("MongoManager: Table "+table.getName()+ " dropped.");
	}

	//************************************************************
	// METHODS FOR DISPOSAL LIST
	//************************************************************
	public boolean insertToDisposalList(User user, String pid, String date, String title) {
        this.selectCollection(DB_DISPOSAL);
        
        boolean success = false;
        long timestamp = 0;
        String username = "";
        String objName = "";
        String objType = "";
        String organization = "";

        if (user != null) {
            objName = Osa.searchManager.getItemByPid(pid, user, user.getLocale()).replace("[", "").replace("]", "");
            objType = Osa.searchManager.getObjectType(pid, user);
            username = user.getCn();
            organization = user.getOrganization().getName();
            
        } else {
            // when scheduled task executed, user not defined
            objName = title;
            // get type from pid
            String pidType = pid.split(":")[1].split("-")[0];
            if (RepositoryManager.DATATYPES_MAP.containsKey(pidType)) {
                objType = RepositoryManager.DATATYPES_MAP.get(pidType);
            }
            // automatic
            username = DestructionPlan.DESTRUCTIONREASON_Scheduled;
            // get organization from pid
            organization = pid.split(":")[0].toLowerCase();
        }
        
        Vector<HashMap<String, String>> pidVector = new Vector<HashMap<String, String>>();
		HashMap<String, String> pidDatamap = new HashMap<String, String>();
        
        BasicDBObject query = new BasicDBObject();
        DBCursor cursor = table.find(new BasicDBObject("organization", organization), new BasicDBObject(ARRAY_DISPOSAL,1).append("_id",false));
        
        if (cursor.size() != 0) {
            success = this.updateDisposalList(pid, objName, date, user);
            
        } else {
        	try {
    			timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(date).getTime();
    		} catch (ParseException e1) {
    			e1.printStackTrace();
    		}
        	pidDatamap.put("pid", pid);
			pidDatamap.put("objname", objName);
			pidDatamap.put("objtype", objType);
			pidDatamap.put("username", username);
			pidDatamap.put("date", date);
			pidDatamap.put("timestamp", String.valueOf(timestamp));
			pidVector.add(pidDatamap);
			
			query.put("organization", organization);
			query.put(ARRAY_DISPOSAL, pidVector);
			
            this.table.insert(query);
            success = true;
        }
        cursor.close();
        return success;
    }
	
	public boolean updateDisposalList(String pid, String objectName, String disposalDate, User user) {
		this.selectCollection(DB_DISPOSAL);
		
		boolean success = false;
		boolean exists 	= false;
		long timestamp 	= 0;
		String organization = "";
		String username = "";
		String objType = "";
		
		if (user != null) {
		    organization = user.getOrganization().getName();
		    username = user.getCn();
		    objType = Osa.searchManager.getObjectType(pid, user);
		} else {
            // get organization from pid
            organization = pid.split(":")[0].toLowerCase();
            //
            username = DestructionPlan.DESTRUCTIONREASON_Scheduled;
            // get type from pid
            String pidType = pid.split(":")[1].split("-")[0];
            if (RepositoryManager.DATATYPES_MAP.containsKey(pidType)) {
                objType = RepositoryManager.DATATYPES_MAP.get(pidType);
            }
		}
				
		DBCursor existsCursor = table.find(new BasicDBObject("organization", organization), new BasicDBObject(ARRAY_DISPOSAL,1).append("_id",false));
		
		while (existsCursor.hasNext()) {
			if (existsCursor.next().get(ARRAY_DISPOSAL).toString().contains(pid)){
				exists = true;
			}
		}
		existsCursor.close();
		
		if (!exists) {
			try {
				timestamp = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(disposalDate).getTime();
			} catch (ParseException e1) {
				e1.printStackTrace();
			}
			
			BasicDBObject updateQuery = new BasicDBObject("organization", organization);
			BasicDBObject updateCommand = new BasicDBObject();

			updateCommand.put("$push", 
							new BasicDBObject(ARRAY_DISPOSAL, new BasicDBObject("pid",pid)
							.append("objname", objectName)
							.append("objtype", objType)
							.append("username", username)
							.append("date", disposalDate)
							.append("timestamp", String.valueOf(timestamp))));
			
			this.table.update(updateQuery, updateCommand);
			success = true;
		}
		
		return success;
	}
	
    /**
     * Remove object from disposal list
     * @param pid       	pid of the object to be deleted
     * @param organization  Name of the user organization
     */
    public void deleteFromDisposalList(String pid, String organization) {
        this.selectCollection(DB_DISPOSAL);
        BasicDBObject updateQuery = new BasicDBObject("organization", organization);
        BasicDBObject updateCommand = new BasicDBObject(ARRAY_DISPOSAL, new BasicDBObject("pid", pid));
        this.table.update(updateQuery, new BasicDBObject("$pull", updateCommand));
    }
        
    public List<DisposalListItem> getDisposalListItems(String organization){
    	this.selectCollection(DB_DISPOSAL);
    	List<DisposalListItem> disposalListItems = new ArrayList<DisposalListItem>();
        DBCursor cursor = table.find(new BasicDBObject("organization", organization), new BasicDBObject("_id", 0));
        
        if (cursor.size() > 0) {
            while (cursor.hasNext()) {
            	List<Map<String,String>> results = new JSONDeserializer<List<Map<String,String>>>().deserialize(cursor.next().get(ARRAY_DISPOSAL).toString());
	            for (int i = 0; i < results.size(); i++) {
	            	Map<String, String> item = results.get(i);
	            	DisposalListItem disposalItem = new DisposalListItem();
	            	
	            	disposalItem.setPid(item.get("pid"));
	            	disposalItem.setObjectName(item.get("objname"));
	            	disposalItem.setObjectType(item.get("objtype"));
	            	disposalItem.setDeleter(item.get("username"));
	            	disposalItem.setDisposalDateString(item.get("date"));
	            	disposalItem.setDisposalDateTimestamp(Long.parseLong(item.get("timestamp")));
	            	
					disposalListItems.add(disposalItem);
	            }
            }
        }
        cursor.close();
        return disposalListItems;
    }
    
    public int getDisposalListSize(String organization) {
    	this.selectCollection(DB_DISPOSAL);
    	//TODO Update mongo jar to 2.2 and use aggregation framework
    	DBCursor cursor = table.find(new BasicDBObject("organization", organization), new BasicDBObject(ARRAY_DISPOSAL,1).append("_id",false));
	    int size = 0;
	    
	    while (cursor.hasNext()) {
	        Map<String,List<Map<String,String>>> result = new JSONDeserializer<Map<String,List<Map<String,String>>>>().deserialize(cursor.next().toString());
	        List<Map<String,String>> map = result.get(ARRAY_DISPOSAL);
	        size += map.size();
	    }
	    cursor.close();
    	return size;
    }
    
	//************************************************************
	// METHODS FOR LATEST EDITED OBJECTS
	//************************************************************
    
    public void addToLatestEdited(User user, String pid, String objectName, String objectId, String eventType) {
    	this.selectCollection(DB_LATESTEDITS);
    	String username = user.getOrganization().getName()+":"+user.getMail();
    	String objName = objectName;
    	
    	Vector<HashMap<String, String>> pidVector = new Vector<HashMap<String, String>>();
		HashMap<String, String> pidDatamap = new HashMap<String, String>();
		
		BasicDBObject query = new BasicDBObject();
		DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_LATESTEDITS,1).append("_id",false)).sort(new BasicDBObject("editDate",1));
		if (cursor.size() != 0) {
			BasicDBObject updateQuery = new BasicDBObject("username", username);
			BasicDBObject updateCommand = new BasicDBObject();
			while (cursor.hasNext()) {
				// If updated object is not already on the list, update list
				if (!cursor.next().get(ARRAY_LATESTEDITS).toString().contains(pid)) {
							
					updateCommand.put("$push", new BasicDBObject(ARRAY_LATESTEDITS, 
							new BasicDBObject("pid",pid)
								.append("name", objName)
								.append("id", objectId)
								.append("event", eventType)
								.append("eventLocal", new LocalizableMessage("general."+eventType).getMessage(user.getLocale()))
								.append("editDate", String.valueOf(System.currentTimeMillis()))));
					// If the list has 12 items, remove last one (oldest)
					if (getLatestEditedCount(user) == 12) {
						BasicDBObject removeLastCommand = new BasicDBObject();
						removeLastCommand.put("$pop", new BasicDBObject(ARRAY_LATESTEDITS, -1));
						this.table.update(updateQuery, removeLastCommand);
					}
			
					this.table.update(updateQuery, updateCommand);
				} else {
					//TODO Refactor and use $set to update...
					updateCommand.put("$pull", new BasicDBObject(ARRAY_LATESTEDITS, new BasicDBObject("pid",pid)));
					this.table.update(updateQuery, updateCommand);
					
					updateCommand = new BasicDBObject();
					updateCommand.put("$push", new BasicDBObject(ARRAY_LATESTEDITS, 
							new BasicDBObject("pid",pid)
								.append("name", objName)
								.append("id", objectId)
								.append("event", eventType)
								.append("eventLocal", new LocalizableMessage("general."+eventType).getMessage(user.getLocale()))
								.append("editDate", String.valueOf(System.currentTimeMillis()))
								));
					this.table.update(updateQuery, updateCommand);
				}
			}
		// If list is empty, create new vector of objects
		} else {
			pidDatamap.put("pid", pid);
			pidDatamap.put("name", objName);
			pidDatamap.put("id", objectId);
			pidDatamap.put("event", eventType);
			pidDatamap.put("eventLocal", new LocalizableMessage("general."+eventType).getMessage(user.getLocale()));
			pidDatamap.put("editDate", String.valueOf(System.currentTimeMillis()));
			pidVector.add(pidDatamap);
			
			query.put("username", username);
			query.put(ARRAY_LATESTEDITS, pidVector);
			
			this.table.insert(query);
		}
		
		cursor.close();
    }
    
    /** Gets latest objects user has edited
     * @param	user	Username
     * @return	Object info in JSON
     */
    public String getLatestEdited(User user) {
    	String jsonresult 	= "";
    	String username 	= user.getOrganization().getName()+":"+user.getMail();
		this.selectCollection(DB_LATESTEDITS);
		// Exclude database row id from results with .append("_id",false)
		DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_LATESTEDITS,1).append("_id",false)).sort(new BasicDBObject("editDate", 1));
		
		if (cursor.size() > 0) {
			while (cursor.hasNext()) {
				jsonresult = "["+cursor.next().toString();
			}
		}

		if (cursor.size() > 0) {
			jsonresult += "]";
		}
		
		cursor.close();
		return jsonresult;
    }
    
    public int getLatestEditedCount(User user) {
	    this.selectCollection(DB_LATESTEDITS);
	    String username = user.getOrganization().getName()+":"+user.getMail();
	    DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_LATESTEDITS,1).append("_id",false));
	    int size = 0;
	    
	    while (cursor.hasNext()) {
	        Map<String,List<Map<String,String>>> result = new JSONDeserializer<Map<String,List<Map<String,String>>>>().deserialize(cursor.next().toString());
	        List<Map<String,String>> map = result.get(ARRAY_LATESTEDITS);
	        size += map.size();
	    }
	    
	    cursor.close();
		return size;
	}
    
    //************************************************************
    // METHODS FOR METADATA FILES
    //************************************************************
    
    public void addMetadataFile(User user, String filename, LinkedHashMap<String, MetaDataElement> map, boolean massEdit) {
        String username = user.getOrganization().getName()+":"+user.getMail();
    	this.selectCollection(DB_METADATA);
    	
    	BasicDBObject updateQuery = new BasicDBObject("username", username);
    	BasicDBObject updateCommand = new BasicDBObject();
    	BasicDBObject query = new BasicDBObject();
    	
    	DBCursor cursor = table.find(updateQuery, new BasicDBObject(ARRAY_METADATA,1).append("_id",false));
    
    	Vector<Object> metadatafiles = new Vector<Object>();
    	
    	MetaDataElement mdElem = new MetaDataElement();
    	mdElem.setName("filename");
    	mdElem.setValue(filename);
    	map.put("filename", mdElem);
    
    	Object serializedMap = null;
    
    	// User already has at least one file added to metadata list
    	if (cursor.size() != 0) {
    	    // Given file is not yet on the list, update db with the file
    	    if (!cursor.next().get(ARRAY_METADATA).toString().contains(filename)) {
                serializedMap = JSON.parse(new flexjson.JSONSerializer().deepSerialize(map));
                updateCommand.put("$push", new BasicDBObject(ARRAY_METADATA, serializedMap));
                this.table.update(updateQuery, updateCommand);
    	    } 
    	    // Given file is already on the list, update it's metadatas
    	    else {
    	        
                LinkedHashMap<String, MetaDataElement> oldMap = getMetadataFile(user, filename);
                // file has already data in mongo
                if (oldMap.size() > 0 && massEdit) {
                    // keep not modified metadata in database
                    for (Entry<String, MetaDataElement> entry : oldMap.entrySet()) {
                        if (!map.containsKey(entry.getKey())) {
                            map.put(entry.getKey(), entry.getValue());
                        }
                    }
                }
    			
                serializedMap = JSON.parse(new flexjson.JSONSerializer().deepSerialize(map));
                updateCommand = new BasicDBObject();
                // Appends filename to filter so query is "get from this user where filename is this"
                updateQuery.append(ARRAY_METADATA+".filename.value", filename);
                updateCommand.put("$set", new BasicDBObject(ARRAY_METADATA+".$", serializedMap));
                  
                this.table.update(updateQuery, updateCommand, true, false);
            }
    	} 
    	// User does not have any files added to metadata list
    	else {
            serializedMap = JSON.parse(new flexjson.JSONSerializer().deepSerialize(map));
            metadatafiles.add(serializedMap);
            query.put("username", username);
            query.put(ARRAY_METADATA, metadatafiles);
            this.table.insert(query);
    	}
    	
    	cursor.close();
    	
    }
    
    public LinkedHashMap<String, MetaDataElement> getMetadataFile(User user, String filename) {
        String username = user.getOrganization().getName()+":"+user.getMail();
    	this.selectCollection(DB_METADATA);
    	DBCursor cursor = table.find(new BasicDBObject("username", username), 
    									new BasicDBObject(ARRAY_METADATA, 
											new BasicDBObject("$elemMatch", 
												new BasicDBObject("filename.value", filename))));
    	
    	List<LinkedHashMap<String, MetaDataElement>> resultSet = new ArrayList<LinkedHashMap<String, MetaDataElement>>();
    	
    	ObjectMapper objMapper = new ObjectMapper();
    	// Prevents mapper from throwing exception on fields that are not present in given TypeReference
    	// In this case returned JSON has field "class" that is not found in MetaDataElement class
    	// Note: This is for jackson version 1.x.x. For 2.x use DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES
    	objMapper.configure(Feature.FAIL_ON_UNKNOWN_PROPERTIES, false);
    	
    	while (cursor.hasNext()) {
    		// Deserializes returned JSON to given data structure
    		resultSet = objMapper.convertValue(cursor.next().get(ARRAY_METADATA), new TypeReference<List<LinkedHashMap<String,MetaDataElement>>>() { });
    	}
    	if (resultSet == null || resultSet.isEmpty()) {
    		return new LinkedHashMap<String, MetaDataElement>();
    	}
    	// Remove filename entry. Can be left in if needed.
    	resultSet.get(0).remove("filename");
    	cursor.close();
    	return resultSet.get(0);
    	
    }
    
    public void removeMetadataFile(User user, String filename) {
        String username = user.getOrganization().getName()+":"+user.getMail();
        this.selectCollection(DB_METADATA);
        
        // remove file data
        BasicDBObject updateObject = new BasicDBObject().append("$pull", new BasicDBObject(ARRAY_METADATA,new BasicDBObject("filename.value",filename)));
        BasicDBObject updateQuery = new BasicDBObject("username", username);
        table.update(updateQuery, updateObject);
    }
    
    public boolean checkIfHasMetadata(User user, String filename) {
        String username = user.getOrganization().getName()+":"+user.getMail();
    	this.selectCollection(DB_METADATA);
    	boolean hasMetadata = false;
    	
    	DBCursor cursor = table.find(new BasicDBObject("username", username), new BasicDBObject(ARRAY_METADATA,1).append("_id",false));
    	
    	while (cursor.hasNext()) {
    		if (cursor.next().get(ARRAY_METADATA).toString().contains(filename)) {
    		    hasMetadata = true;
    		}
    	}
    	cursor.close();
    	return hasMetadata;
    }    
} public abstract class RepositoryManager {
    
    // Fedora versions
    public static final String FEDORA3 = "fedora3";
    public static final String FEDORA4 = "fedora4";
    
    // content model prefix
    public static final String CONTENTMODEL    = "osa-system:contentmodel-";
    
    // data types
    public static final String ACTION          = "action";
    public static final String AGENT           = "agent";
    public static final String APPLICATION     = "application";
    public static final String AUDIO           = "audio";
    public static final String BASECOLLECTION  = "basecollection";
    public static final String COLLECTION      = "collection";
    public static final String DEFAULT_TYPE    = "default";
    public static final String DOCUMENT        = "document";
    public static final String DRAWING         = "drawing";
    public static final String EVENT           = "event";    
    public static final String GROUPCOLLECTION = "groupcollection";
    public static final String IMAGE           = "image";
    public static final String MAP             = "map";
    public static final String MOVINGIMAGE     = "movingimage";
    public static final String PHYSICALOBJECT  = "physicalobject";
    public static final String PLACE           = "place";
    public static final String ROOT            = "root";    
    public static final String UNITCOLLECTION  = "unitcollection";
    
    public static final String PID_COLLECTION_AGENTS         = ":C-agents";
    public static final String PID_COLLECTION_ACTIONS        = ":C-actions";
    public static final String PID_COLLECTION_CONTEXTS       = ":C-contexts";
    public static final String PID_COLLECTION_EVENTS         = ":C-events";
    public static final String PID_COLLECTION_LYACTIONS      = ":C-lyactions";
    public static final String PID_COLLECTION_PLACES         = ":C-places";
    public static final String PID_COLLECTION_TOL2008ACTIONS = ":C-tol2008actions";
    
    /** Abbreviations for object types. Lookup with PID's object type's identifier.
     * 
     */
    public static final Map<String, String> DATATYPES_MAP = 
            Collections.unmodifiableMap(new HashMap<String, String>() {
                private static final long serialVersionUID = 1L;
            { 
                put("F", RepositoryManager.ACTION);
                put("W", RepositoryManager.AGENT);
                put("S", RepositoryManager.APPLICATION);
                put("A", RepositoryManager.AUDIO);
                put("C", RepositoryManager.COLLECTION);
                put("T", RepositoryManager.DOCUMENT);
                put("D", RepositoryManager.DRAWING);
                put("E", RepositoryManager.EVENT);
                put("P", RepositoryManager.IMAGE);
                put("M", RepositoryManager.MAP);
                put("V", RepositoryManager.MOVINGIMAGE);
                put("O", RepositoryManager.PHYSICALOBJECT);
                put("L", RepositoryManager.PLACE);
                put("X", RepositoryManager.DEFAULT_TYPE);
            }});

    /** Letters for different kind of object types. Lookup with object type as plain, normal text.
     * 
     */
    public static final Map<String, String> DATATYPES_MAP_REVERSE = 
            Collections.unmodifiableMap(new HashMap<String, String>() {
                private static final long serialVersionUID = 1L;
            { 
                put(RepositoryManager.ACTION,        "F");
                put(RepositoryManager.AGENT,         "W");
                put(RepositoryManager.APPLICATION,   "S");
                put(RepositoryManager.AUDIO,         "A");
                put(RepositoryManager.COLLECTION,    "C");
                put(RepositoryManager.DOCUMENT,      "T");
                put(RepositoryManager.DRAWING,       "D");
                put(RepositoryManager.EVENT,         "E");
                put(RepositoryManager.IMAGE,         "P");
                put(RepositoryManager.MAP,           "M");
                put(RepositoryManager.MOVINGIMAGE,   "V");
                put(RepositoryManager.PHYSICALOBJECT,"O");
                put(RepositoryManager.PLACE,         "L");
                put(RepositoryManager.DEFAULT_TYPE,  "X");
            }});

    public static final Map<String, String> CONTEXTOBJECT_COLLECTIONS = 
            Collections.unmodifiableMap(new HashMap<String, String>() {
                private static final long serialVersionUID = 1L;
            { 
                put(RepositoryManager.ACTION, RepositoryManager.PID_COLLECTION_ACTIONS);
                put(RepositoryManager.AGENT, RepositoryManager.PID_COLLECTION_AGENTS);
                put(RepositoryManager.EVENT, RepositoryManager.PID_COLLECTION_EVENTS);
                put(RepositoryManager.PLACE, RepositoryManager.PID_COLLECTION_PLACES);
            }});
    
    private static final Logger logger = Logger.getLogger(RepositoryManager.class);
    
    public String username;
    public String password;
    public String baseUrl;
    // content data
    Vector<FedoraBean> vectFedoraObjects = new Vector<FedoraBean>();
    
    public abstract void createAttachmentDatastream(File file, String pid, FedoraBean fObject, String user);
    public abstract void createPreviewDatastream(String filePath, String pid, String datastreamName, String user);
    public abstract FedoraBean createFedoraBean(boolean bIngest, User user, Gui userGui, String pid, String type, LinkedHashMap<String, MetaDataElement> lhmElements);
    public abstract void createRootObject(String rootName, Gui userGui, User user);
    public abstract String getAuditTrail(String pid);
    public abstract void deleteObject(String pid);
    public abstract void createResidualObject(String pid, DestructionPlan plan);
    public abstract void deleteRootObject(String rootName);
    public abstract void deleteDataStream(String pid, String dsid);
    public abstract void deleteAllobjects(String orgContext);
    public abstract void deleteActionObjects(String orgContext, String type);
    public abstract List<String> findAllObjects(String orgContext);
    public abstract FedoraBean findObject(String pid);
    public abstract String getChildren(String pid);
    public abstract ArrayList<HashMap<String, String>> getAllChildren(String pid);
    public abstract String getCollectionObjects(String organization);
    public abstract Document getSchema(String type, String organization);
    public abstract String getNextPID(String organizationName, String mime);
    public abstract List<MetaDataElement> getObjectHistory(String pid);
    public abstract String ingest(Gui userGui, User user, String organization, File file, List<File> attachment, 
        String type, String pid, LinkedHashMap<String, MetaDataElement> lhmCaptureElements, FedoraBean oldFedoraBean) throws SAXException;
    public abstract void ingestWithPid(boolean bIngest, User user, Gui userGui, String organization, File file, String type, LinkedHashMap<String, MetaDataElement> lhmElements);
    public abstract boolean ingestFile(String organization, String path);
    public abstract boolean ingestObject(boolean bIngest, User user, String organization, String type, File file, List<File> attachment, FedoraBean fedoraBean) throws SAXException;
    public abstract void moveObject(String pid, String oldParent, String newParent, User user) throws FedoraClientException;
    public abstract void updateObjectPosition(String pid, boolean isChild, String oldParent, String newParent, User user) throws FedoraClientException;
    public abstract String getPath(String pid);
    public abstract int getObjLevel(String collectionpid);
    public abstract List<String> listDataStreams(String pid);
    public abstract List<DatastreamType> listRawDataStreams(String pid);
    public abstract void purgeRelationship(String pid, String relationship, String object, boolean isLiteral);
    public abstract String cleanEntry(String entry);
    public abstract void purgeRelationshipsFromObjects(String pid);
    public abstract void setObjectState(String pid, State state);
    public abstract void writeDatastreamToFile(String uploadDir, String pid, String datastreamName, String origExt) throws UnsupportedEncodingException, IOException;
    
    /**
     * Check if datastreams have been modified before updating to Fedora repository
     * @param oldObject     current object in Fedora Repository
     * @param newObject     new object to be updated to Fedora Repository
     * @return              object updated with boolean values, which tells if datastreams modified
     */
    public FedoraBean checkIfDatastreamsModified(FedoraBean oldObject, FedoraBean newObject) {
        
        if (oldObject == null) {
            return newObject;
        }
        
        // get existing datastreams for pid
        List<String> existingDatastreams = listDataStreams(oldObject.getPID());
        int size = existingDatastreams.size();
        
        // Handle all datastreams
        for (int i=0; i<size; i++) {
            
            String currentDatastream = existingDatastreams.get(i);
            String newDS = null;
            String oldDS = null;
            
            if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_CAPTURE)) {
                newDS = newObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().toString();                
                oldDS = oldObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().toString();
                
            } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_DC)) {
                
                // sort by key
                List<String> keysNew = new ArrayList<String>(newObject.getDataStream(FedoraBean.DATASTREAM_DC).getMetaDataElements().keySet());
                Collections.sort(keysNew);
             
                Map<String, MetaDataElement> tempNew = new LinkedHashMap<String, MetaDataElement>();
                for (String key: keysNew) {
                    tempNew.put(key, newObject.getDataStream(FedoraBean.DATASTREAM_DC).getMetaDataElements().get(key));
                }
                
                List<String> keysOld = new ArrayList<String>(oldObject.getDataStream(FedoraBean.DATASTREAM_DC).getMetaDataElements().keySet());
                Collections.sort(keysOld);
             
                Map<String, MetaDataElement> tempOld = new LinkedHashMap<String, MetaDataElement>();
                for (String key: keysOld) {
                    tempOld.put(key, oldObject.getDataStream(FedoraBean.DATASTREAM_DC).getMetaDataElements().get(key));
                }

                // remove name=identified before comparing datastreams
                tempOld.remove("identifier");
                
                newDS = tempNew.toString();
                oldDS = tempOld.toString();
                
            } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_RELS_EXT)) {
                
                LinkedHashMap<String, MetaDataElement> tempNew = newObject.getDataStream(FedoraBean.DATASTREAM_RELS_EXT).getMetaDataElements();
                LinkedHashMap<String, MetaDataElement> tempOld = oldObject.getDataStream(FedoraBean.DATASTREAM_RELS_EXT).getMetaDataElements();
                
                // remove name=hasModel before comparing datastreams
                tempOld.remove("hasModel");
                
                newDS = tempNew.toString();
                oldDS = tempOld.toString();
                
            } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_MANAGEMENT)) {
                
                newDS = newObject.getDataStream(FedoraBean.DATASTREAM_MANAGEMENT).getMetaDataElements().toString();
                oldDS = oldObject.getDataStream(FedoraBean.DATASTREAM_MANAGEMENT).getMetaDataElements().toString();
                
            } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_RELS_INT)) {
                // TODO: check this
            }
            
            MessageDigest m;
            String newChecksum = null;
            String oldChecksum = null;
            
            try {
                
                m = MessageDigest.getInstance("MD5");
                
                if (oldDS != null) {
                    m.update(oldDS.getBytes(), 0, oldDS.length());
                    oldChecksum = new BigInteger(1,m.digest()).toString(16);
                    m.reset();
                }
                
                if (newDS != null) {
                    m.update(newDS.getBytes(), 0, newDS.length());
                    newChecksum = new BigInteger(1,m.digest()).toString(16);
                }
                
                if ((newChecksum != null && oldChecksum != null && !oldChecksum.equalsIgnoreCase(newChecksum))
                    || newChecksum != null && oldChecksum == null
                   )
                {
                    if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_CAPTURE)) {
                        // Datastream modified
                        
                        // Check if title has been edited, update it to basket items
                        if (newObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().containsKey(CaptureBean.TITLE) && 
                            oldObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().containsKey(CaptureBean.TITLE)) {
                            if (!newObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().get(CaptureBean.TITLE).getValue().equals(
                                 oldObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().get(CaptureBean.TITLE).getValue())) {
                                Osa.dbManager.get("mongo").syncBaskets(newObject.getPID(), 
                                                                       MongoManager.EVENT_RENAME, 
                                                                       newObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().get(CaptureBean.TITLE).getValue());
                            }
                        }
                        
                        newObject.bUpdateCaptureDatastream = true;
                        // store old parent (value is used when updating path, if object and child objects moved)
                        String oldParent = oldObject.getDataStream(FedoraBean.DATASTREAM_CAPTURE).getMetaDataElements().get(CaptureBean.RELATION_IsPartOf).getValue();
                        newObject.setOldParent(oldParent);
                        
                    } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_DC)) {
                        // datastream modified
                        newObject.bUpdateDcDatastream = true;
                    } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_RELS_EXT)) {
                        // datastream modified
                        newObject.bUpdateReslExtDatastream = true;
                    } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_RELS_INT)) {
                        // datastream modified
                        newObject.bUpdateReslIntDatastream = true;
                    } else if (currentDatastream.equalsIgnoreCase(FedoraBean.DATASTREAM_MANAGEMENT)) {
                        // datastream modified
                        newObject.bUpdateManagementDatastream = true;
                    }
                }
                
            } catch (NoSuchAlgorithmException e) {
                logger.error("RepositoryManager error: "+e);
            }
        }
        
        return newObject;
    }
    
    /**
     * Get fedora object from FedoraManager
     * @param pid       The PID of the object.
     * @return          fedora object
     */
    public FedoraBean getObject(String pid) {
        Iterator<FedoraBean> iterFedoraObjects = this.vectFedoraObjects.iterator();
        FedoraBean fedoraObject = null;        
        
        while (iterFedoraObjects.hasNext()) {
            FedoraBean currentObject = (FedoraBean) iterFedoraObjects.next();
            
            if (currentObject.getPID().equalsIgnoreCase(pid)) {
                fedoraObject = currentObject;
            }
        }
        return fedoraObject;
    }
    
    /**
     * Check if object is allowed in storage level
     * @param plan          Preservation plan
     * @param objectType    document/image/base collection/group collection/ etc.
     * @param order         storage level if available (default -1)
     * @param objParent     pid of parent object
     * @param isCollection  parameter to define if object is collection
     * @return              validation error text if object can not be stored
     */
    public String isObjectPermitted(PreservationPlan plan, String objectType, int order, String objParent, boolean isCollection) {
        String validationErrorText = "";
        if (objectType == null && objParent == null) {
            return "error.preservation";
        } else if (objParent == null && order == -1) {
            return "error.preservation";
        } else if (objParent != null && order == -1) {
            order = this.getObjLevel(objParent);
        }
        
        if (isCollection) {
            // collection itself has information in preservation plan
            order = order+1;
        }
        
        // check preservation plan
        if (order <= plan.getMaxLevel()) {
            for (ArchiveLevel level : plan.getHierarchy()) {
                if (order == level.getOrder() || order < level.getMaxOrder()) {
                    if (isCollection) {
                        // adding collection
                        // check type of collection, and check if it is allowed to this level
                        if (!level.getAllowCollectionsForType(objectType)) {
                            validationErrorText = "error.preservation.collection";
                        }
                        break;
                        
                    } else {
                        // adding other type
                        // check type of objParent, and getAllowDocuments according to this value                     
                        if (!level.getAllowDocumentsForType(objectType)) {
                            validationErrorText = "error.preservation";
                        }
                        break;
                    }
                }
            }
        } else {
            validationErrorText = "max level is "+ plan.getMaxLevel();
        }
        return validationErrorText;
    }
} public class DisposalListItem implements Serializable {

    private static final long serialVersionUID = -4165180772198290183L;
    private String pid;
	private String objectName;
	private String objectType; 
	private String deleter;
	private String disposalDateString;
	private long disposalDateTimestamp;
	
	public DisposalListItem(){
	    this.pid = "";
        this.objectName = "";
        this.objectType = "";
        this.deleter = "";
        this.disposalDateString = "";
        this.disposalDateTimestamp = 0;
	}
	
	public DisposalListItem(String pid, String objectName, String objectType, String deleter, String disposalDateString) {
		this.pid = pid;
		this.objectName = objectName;
		this.objectType = objectType;
		this.deleter = deleter;
		this.disposalDateString = disposalDateString;
	}

	public DisposalListItem(String pid, String objectName, String objectType, String deleter, String disposalDateString, long disposalDateTimestamp) {
		this.pid = pid;
		this.objectName = objectName;
		this.objectType = objectType;
		this.deleter = deleter;
		this.disposalDateString = disposalDateString;
		this.disposalDateTimestamp = disposalDateTimestamp;
	}

	public String getPid() {
		return pid;
	}
	public void setPid(String pid) {
		this.pid = pid;
	}
	public String getObjectName() {
		return objectName;
	}
	public void setObjectName(String objectName) {
		this.objectName = objectName;
	}
	public String getObjectType() {
		return objectType;
	}
	public void setObjectType(String objectType) {
		this.objectType = objectType;
	}
	public String getDeleter() {
		return deleter;
	}
	public void setDeleter(String deleter) {
		this.deleter = deleter;
	}
	public String getDisposalDateString() {
		return disposalDateString;
	}
	public void setDisposalDateString(String disposalDateString) {
		this.disposalDateString = disposalDateString;
	}
	public long getDisposalDateTimestamp() {
		return disposalDateTimestamp;
	}
	public void setDisposalDateTimestamp(long disposalDateTimestamp) {
		this.disposalDateTimestamp = disposalDateTimestamp;
	}

} public class Gui implements Serializable {

    private static final long serialVersionUID = -6157579742585725399L;
    private static final Logger logger = Logger.getLogger(Gui.class);
    
    // Form variables
    public static final String FORM_SEARCH         = "search";
    public static final String DEFAULT_MAIN_HEADER = "/layout/osa_header_main.jsp";
    public static final String DEFAULT_SUB_HEADER  = "/layout/osa_header.jsp";
    public static final String DEFAULT_APP_TITLE   = "OSA Web Client";
    
    // Layout
    private String cssFile 	   = null;
    private String logoFile    = null;
    private String logoFileAlt = null;
    private String appTitle    = DEFAULT_APP_TITLE;
    private String mainHeader  = DEFAULT_MAIN_HEADER;
    private String subHeader   = DEFAULT_SUB_HEADER;
    
    private String resultType  = "";
    private int queueSize;
    private int initialBasecollectionId = 0;
    
    // map between FormElement and datastream
    public MultiValueMap mapDataStreams = null;
    
    private SearchConfiguration searchConfiguration = null;    
    private SearchResultConfiguration searchResults = null;
    // contains all FormElements to be shown in GUI
    private FormConfiguration formConfiguration 	= null;
    
    // Disposal
    private String disposeMethod = "";
    private boolean disposeChilds = false;
    
    public Gui(File xmlConfiguration, OsaActionBeanContext context) {
        
        mapDataStreams = new MultiValueMap();
        searchConfiguration = new SearchConfiguration();
        
        try {
            DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
            DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
            Document doc = docBuilder.parse(xmlConfiguration);
            doc.getDocumentElement().normalize();
            getIngestForms(doc);
            getSearchForm(doc);
            getResultLayout(doc);
            getLayout(doc, context);
            getUploadConfig(doc);
            getIdGenerationConfig(doc);
            getFacetFieldsFromConf(doc);
            getFacetFieldsforAdvBrowse(doc);
        } catch (Exception e) {
            logger.error("Failed to load configuration file: " + xmlConfiguration+", "+ e);
        }
    }

    /**
     * Returns all datastreams for the form element name
     * Reads <metadata> tags in osa-system:contentmodel-*
     * @param name      name of the form element
     * @return          list of datastreams
     */
    public List<String> getDataStreamMappings(String name) {
        List<String> retValue = new ArrayList<String>();
        List<MetaDataElement> list;
        Set entrySet = this.mapDataStreams.entrySet();
        Iterator it = entrySet.iterator();
               
        while (it.hasNext()) {
            Map.Entry mapEntry = (Map.Entry) it.next();
            list = (List<MetaDataElement>) this.mapDataStreams.get(mapEntry.getKey());
                        
            for (int i=0; i<list.size(); i++) {
                if (name.equalsIgnoreCase((String) mapEntry.getKey())) {
                    MetaDataElement el = (MetaDataElement)list.get(i);
                    retValue.add(el.getDatastream());
                }
            }
        }
        
        return retValue;
    }
    
    /**
     * Find right name for element according to name in form (keyName)
     * @param keyName           element visible name in form
     * @param elementName       element name
     * @param dataStream        datastream name
     * @return
     */
    public String getMetadataName(String elementName, String dataStream) {
        String retValue = null;
        List<MetaDataElement> list;
        Set entrySet = this.mapDataStreams.entrySet();
        Iterator it = entrySet.iterator();
               
        while (it.hasNext()) {
            Map.Entry mapEntry = (Map.Entry) it.next();
            list = (List<MetaDataElement>) this.mapDataStreams.get(mapEntry.getKey());
            
            for (int i=0; i<list.size(); i++) {
                MetaDataElement el = (MetaDataElement)list.get(i);
                if (el.getDatastream().equalsIgnoreCase(dataStream) && el.getVisibleName().equalsIgnoreCase(elementName)) {
                    retValue = (String) mapEntry.getKey();
                    break;
                }
            }
        }
        
        return retValue;
    }
    
    /**
     * Find attribute visible name used in datastream
     * @param name          attribute name
     * @param dataStream    datastream name
     * @return              visible name
     */
    public String getVisibleName(String name, String dataStream) {
        String retValue = null;
        List<MetaDataElement> list;
        Set entrySet = this.mapDataStreams.entrySet();
        Iterator it = entrySet.iterator();
        
        while (it.hasNext()) {
            Map.Entry mapEntry = (Map.Entry) it.next();
            list = (List<MetaDataElement>) this.mapDataStreams.get(mapEntry.getKey());
                        
            for (int i=0; i<list.size(); i++) {
                if (name.equalsIgnoreCase((String) mapEntry.getKey())) {
                    MetaDataElement el = (MetaDataElement)list.get(i);
                    
                    if (el.getDatastream().equalsIgnoreCase(FedoraBean.DATASTREAM_CAPTURE)) {
                        retValue = el.getVisibleName();
                    }
                }
            }
        }
        
        return retValue;
    }
    
    /**
     * Get GUI elements from xml configuration file
     * @param doc           document
     * @param elementName   name of an element
     */
    public void getElements(Document doc, String elementName, String form) {
        
        String formName = null;
        String tabName = null;
        
        if (form.equalsIgnoreCase(FORM_SEARCH)) {
            NodeList searchNodes = doc.getElementsByTagName("search");
            for (int i=0; i<searchNodes.getLength(); i++) {
                Node searchNode = searchNodes.item(i);
                if (searchNode.getNodeType() == Node.ELEMENT_NODE) {
                    NodeList formNodes = searchNode.getChildNodes();
                    for (int j=0; j<formNodes.getLength(); j++) {
                        Node formNode = formNodes.item(j);
                        if (formNode.getNodeType() == Node.ELEMENT_NODE) {
                            NodeList fieldNodes = formNode.getChildNodes();
                            for (int k=0; k<fieldNodes.getLength(); k++) {
                                Node fieldNode = fieldNodes.item(k);
                                if (fieldNode.getNodeType() == Node.ELEMENT_NODE) {
                                    if (fieldNode.getNodeName().equals("field")) {
                                        this.parseFieldNode(fieldNode, form, null, null);
                                    } else {
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            
        } else {
            try {
                NodeList formsNodes = doc.getElementsByTagName("forms");
                for (int i=0; i<formsNodes.getLength(); i++) {
                    Node formsNode = formsNodes.item(i);
                    if (formsNode.getNodeType() == Node.ELEMENT_NODE) {
                        NodeList formNodes = formsNode.getChildNodes();
                        for (int j=0; j<formNodes.getLength(); j++) {
                            Node formNode = formNodes.item(j);
                            if (formNode.getNodeType() == Node.ELEMENT_NODE) {
                                formName = formNode.getAttributes().getNamedItem("name").getNodeValue();
                                // tabs-list
                                NodeList tabsNodes = formNode.getChildNodes();
                                for (int k=0; k<tabsNodes.getLength(); k++) {
                                    // tabs
                                    Node tabsNode = tabsNodes.item(k);
                                    if (tabsNode.getNodeType() == Node.ELEMENT_NODE) {
                                        // tab-list
                                        NodeList tabNodes = tabsNode.getChildNodes();
                                        for (int l=0; l<tabNodes.getLength(); l++) {
                                            // tab
                                            Node tabNode = tabNodes.item(l);
                                            if (tabNode.getNodeType() == Node.ELEMENT_NODE) {
                                                tabName = tabNode.getAttributes().getNamedItem("name").getNodeValue();
                                                formConfiguration.setTab(formName, tabName);
                                                // field-list
                                                NodeList fieldNodes = tabNode.getChildNodes();
                                                for (int m=0; m<fieldNodes.getLength(); m++) {
                                                    // field
                                                    Node fieldNode = fieldNodes.item(m);
                                                    if (fieldNode.getNodeType() == Node.ELEMENT_NODE) {
                                                        if (fieldNode.getNodeName().equals("field")) {
                                                            this.parseFieldNode(fieldNode, form, formName, tabName);
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                
            } catch (Exception e) {
                logger.error("Gui:getElements() "+e);
            }
        }
    }

    public void parseFieldNode(Node node, String form, String formName, String tabName) {
        
        if (node.getNodeType() != Node.ELEMENT_NODE) {
            return;
        }
        
        if (form.equalsIgnoreCase(FORM_SEARCH)) {
            // Search fields
            SearchFormField searchFormField = new SearchFormField();
            
            try {
                searchFormField.setName(node.getAttributes().getNamedItem("name").getNodeValue());
                searchFormField.setType(SearchFormField.Type.valueOf(node.getAttributes().getNamedItem("type").getNodeValue()));
            }
            catch (Exception ex) {
                logger.error(ex);
            }
            
            // Map visible search fields to backend search fields.
            // In this case Solr is the search provider, 
            // and fields from Solr schema are mapped via the organization configuration.
            NodeList subNodes = node.getChildNodes();
            for (int j=0; j<subNodes.getLength(); j++) {
                Node subNode = subNodes.item(j);
                if (subNode.getNodeType() == Node.ELEMENT_NODE) {
                    // Solr fields bound to the search field
                    if ("solr".equalsIgnoreCase(subNode.getNodeName())) {
                        NodeList solrFieldNodes = subNode.getChildNodes();
                        for (int k=0; k<solrFieldNodes.getLength(); k++) {
                            Node solrFieldNode = solrFieldNodes.item(k);
                            if (solrFieldNode.getNodeType() == Node.ELEMENT_NODE) {
                                SolrField solrField = new SolrField();
                                solrField.setName(solrFieldNode.getAttributes().getNamedItem("name").getNodeValue());
                                if (solrFieldNode.getAttributes().getNamedItem("boost") != null) {
                                    solrField.setBoost(Integer.parseInt(solrFieldNode.getAttributes().getNamedItem("boost").getNodeValue()));
                                }
                                if (solrFieldNode.getAttributes().getNamedItem("ngramBoost") != null) {
                                    solrField.setNgramBoost(Integer.parseInt(solrFieldNode.getAttributes().getNamedItem("ngramBoost").getNodeValue()));
                                }
                                // Read solr fields bound to freetext field in conf file 
                                if ("freetext".equalsIgnoreCase(subNode.getParentNode().getAttributes().getNamedItem("name").getNodeValue())) {
                                    this.searchConfiguration.getFreetextField().add(solrField);
                                } else {
                                    // Solr fields bound to rest of the fields
                                    searchFormField.getSolrFields().add(solrField);
                                }
                                
                            }
                        }
                    }
                    if ("subFields".equalsIgnoreCase(subNode.getNodeName())) {

                        NodeList subNodes2 = subNode.getChildNodes();
                        for (int l=0; l<subNodes2.getLength(); l++) {
                            Node subNode2 = subNodes2.item(l);
                            if (subNode2.getNodeType() == Node.ELEMENT_NODE) {
                                
                                SearchFormField subField = new SearchFormField();
                                subField.setName(subNode2.getAttributes().getNamedItem("name").getNodeValue());
                                subField.setType(SearchFormField.Type.valueOf(subNode2.getAttributes().getNamedItem("type").getNodeValue()));
                                
                                // Solr fields bound to the search field
                                if ("solr".equalsIgnoreCase(subNode2.getNodeName())) {
                                    NodeList solrFieldNodes = subNode2.getChildNodes();
                                    for (int m=0; m<solrFieldNodes.getLength(); m++) {
                                        Node solrFieldNode = solrFieldNodes.item(m);
                                        if (solrFieldNode.getNodeType() == Node.ELEMENT_NODE) {
                                            SolrField solrField = new SolrField();
                                            solrField.setName(solrFieldNode.getAttributes().getNamedItem("name").getNodeValue());
                                            if (solrFieldNode.getAttributes().getNamedItem("boost") != null) {
                                                solrField.setBoost(Integer.parseInt(solrFieldNode.getAttributes().getNamedItem("boost").getNodeValue()));
                                            }
                                            if (solrFieldNode.getAttributes().getNamedItem("ngramBoost") != null) {
                                                solrField.setNgramBoost(Integer.parseInt(solrFieldNode.getAttributes().getNamedItem("ngramBoost").getNodeValue()));
                                            }
                                            subField.getSolrFields().add(solrField);
                                        }
                                    }
                                }
                                searchFormField.getSubFields().put(subField.getName(), subField);
                            }
                        }
                    }
                }
            }
            // Add parsed field information to search configuration wrapper
            searchConfiguration.getSearchForm().put(searchFormField.getName(), searchFormField);
            
        } else {    // All other forms
            String name = null;
            FormElement.FieldType fieldType = FormElement.FieldType.undefined;
            List<String> enumValues = null;
            boolean isReadonly = false;
            boolean isGenereted = false;
            boolean isRequired = false;
            String multivalue = "false";
            String source = null;
            String nestingStyle = null;
            Vector<FormElement> nestedElements = new Vector<FormElement>();
            
            // current element name
            name = node.getAttributes().getNamedItem("element").getNodeValue();
            
            if (node.getAttributes().getNamedItem("readonly") != null) {
                isReadonly = Boolean.parseBoolean(node.getAttributes().getNamedItem("readonly").getNodeValue());
            }
            if (node.getAttributes().getNamedItem("generated") != null) {
                isGenereted = Boolean.parseBoolean(node.getAttributes().getNamedItem("generated").getNodeValue());
            }
            if (node.getAttributes().getNamedItem("required") != null) {
                isRequired = Boolean.parseBoolean(node.getAttributes().getNamedItem("required").getNodeValue());
            }
            if (node.getAttributes().getNamedItem("type") != null) {
                fieldType = FormElement.FieldType.getEnum(node.getAttributes().getNamedItem("type").getNodeValue());
                
                if (fieldType.equals(FormElement.FieldType.select) || fieldType.equals(FormElement.FieldType.units)) {
                    // read enum values
                    enumValues = getOptionsList(node, "item");
                    
                } else if (fieldType.equals(FormElement.FieldType.nested) 
                           || fieldType.equals(FormElement.FieldType.timerange)
                           || fieldType.equals(FormElement.FieldType.linked)) {
                    
                	if (node.getAttributes().getNamedItem("nestingStyle") != null) {
                		nestingStyle = node.getAttributes().getNamedItem("nestingStyle").getNodeValue();
                	}
                	
                    NodeList nestedFields = node.getChildNodes();
                    for (int j=0; j<nestedFields.getLength(); j++) {
                        Node nestedNode = nestedFields.item(j);
                        if (nestedNode.getNodeType() == Node.ELEMENT_NODE) {
                            FormElement nestedElement = new FormElement(); 
                            nestedElement.setName(nestedNode.getAttributes().getNamedItem("element").getNodeValue());
                            nestedElement.setReadonly(Boolean.parseBoolean(nestedNode.getAttributes().getNamedItem("readonly").getNodeValue()));
                            nestedElement.setRequired(Boolean.parseBoolean(nestedNode.getAttributes().getNamedItem("required").getNodeValue()));
                            nestedElement.setFieldType(FormElement.FieldType.getEnum(nestedNode.getAttributes().getNamedItem("type").getNodeValue()));
                            if (nestedNode.getAttributes().getNamedItem("type").getNodeValue().equalsIgnoreCase(FormElement.FieldType.select.toString())) {
                                nestedElement.setEnumValues(getOptionsList(nestedNode, "item"));
                            }
                            nestedElements.add(nestedElement);
                        }
                    }
                    
                }
            }
            if (node.getAttributes().getNamedItem("multivalue") != null) {
                
                switch (node.getAttributes().getNamedItem("multivalue").getNodeValue()) {
                    case FormElement.MULTIVALUE_DELIMITED:
                        multivalue = FormElement.MULTIVALUE_DELIMITED;
                        // do something
                        break;
                    case FormElement.MULTIVALUE_MULTIFIELD:
                        multivalue = FormElement.MULTIVALUE_MULTIFIELD;
                        // do something
                        break;
                    default:
                        multivalue = FormElement.MULTIVALUE_NONE; 
                        break;
                }
            }
            if (node.getAttributes().getNamedItem("source") != null) {
                source = node.getAttributes().getNamedItem("source").getNodeValue();
            }
            
            FormElement formElement = new FormElement(name, 
                                                      formName, 
                                                      tabName, 
                                                      fieldType, 
                                                      enumValues, 
                                                      isReadonly, 
                                                      isGenereted, 
                                                      isRequired, 
                                                      multivalue, 
                                                      source, 
                                                      nestingStyle,
                                                      nestedElements);

            formConfiguration.setFormElement(formName, formElement);
        }
    }
    
    /**
     * Get elements to be shown in the form
     * @param formName      name of the form
     * @return              vector of elements
     */
    public LinkedHashMap<String, FormElement> getGUIElements(String formName) {
        return this.formConfiguration.getForm(formName);
    }
    
    /**
     * Get tabs to be shown in the form
     * @param formName      name of the form
     * @return              vector of tab names
     */
    public Vector<String> getGUITabs(String formName) {
        return this.formConfiguration.getTabs(formName);
    }
    
    /**
     * Get enumerated items for field element
     * @param node      field element node
     * @return          list of enumerated items
     */
    private List<String> getOptionsList(Node node, String nodeName) {
        List<String> itemList = new ArrayList<String>();
        
        if (node != null) {
            NodeList nodes = node.getChildNodes();
            
            for (int i=0; i<nodes.getLength(); i++) {
                Node currentNode = nodes.item(i);
                if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
                    // check node name
                    if (currentNode.getNodeName().equalsIgnoreCase(nodeName)) {
                        if (!currentNode.getTextContent().isEmpty()) {
                            itemList.add(currentNode.getTextContent());
                        }
                    } else if (currentNode.getNodeName().equalsIgnoreCase("authoritative_list")) {
                        return getOptionsList(currentNode, nodeName);
                    }
                }
            }
            
        }
        return itemList;
    }

    /**
     * Find ParentNode by type
     * @param node      current node
     * @param nodeType  type of node (f.ex. form, tab, etc.)
     * @return
     * @throws Exception 
     */
    private Node getParentNode(Node node, String nodeType)
    {
        Node parentNode = null;
        if (node.getParentNode() != null) {
            parentNode = node.getParentNode();
            String parentNodeType = node.getParentNode().getNodeName();

            // check if types match
            if (nodeType != parentNodeType) {
                return getParentNode(parentNode, nodeType);
            }
            
        } else {
            parentNode = null;
        }

        return parentNode;
    }
    
    public void getLayout(Document doc, OsaActionBeanContext context) {
        try {
        	NodeList layoutSettings = doc.getElementsByTagName("layout");
        	for (int i=0; i<layoutSettings.getLength(); i++){
        		Node item = layoutSettings.item(i);
        		if (item.getNodeType() == Node.ELEMENT_NODE){
        			NodeList childNodes = item.getChildNodes();
        			for (int j=0; j<childNodes.getLength(); j++) {
        				Node subItem = childNodes.item(j);
        				if (subItem.getNodeName().equals("cssFile")) {
        					this.setCssFile(subItem.getFirstChild().getNodeValue());
        				}
        				if (subItem.getNodeName().equals("logoFile")) {
        					this.setLogoFile(subItem.getFirstChild().getNodeValue());
        					//TODO do empty check for alt attribute
        					this.setLogoFileAlt(subItem.getAttributes().getNamedItem("alt").getNodeValue());
        				}
        				if (subItem.getNodeName().equals("applicationTitle")) {
        					if (subItem.getFirstChild().getNodeValue() != null) {
        						this.setAppTitle(subItem.getFirstChild().getNodeValue());
        					}
        				}
        				
        				// Organization specific header files
        				if (subItem.getNodeName().equals("headerJsp")) {
        					NodeList headerFiles = subItem.getChildNodes();
                			for (int k = 0; k < headerFiles.getLength(); k++) {
                				Node headerFile = headerFiles.item(k);
                				if (headerFile.getNodeName().equals("mainPage")) {
                					// First check if config has setting for header file
                					if(headerFile.getFirstChild().getNodeValue() != null) {
                						File main = new File(context.getServletContext().getRealPath(headerFile.getFirstChild().getNodeValue()));
                						// Then check if file set in config actually exists
                						if (main.exists() && !main.isDirectory()) {
                							this.setMainHeader(headerFile.getFirstChild().getNodeValue());
                						}
                					}
                				}
                				if (headerFile.getNodeName().equals("subPage")) {
                					// First check if config has setting for header file
                					if(headerFile.getFirstChild().getNodeValue() != null) {
                						File sub = new File(context.getServletContext().getRealPath(headerFile.getFirstChild().getNodeValue()));
                						// Then check if file set in config actually exists
                						if (sub.exists() && !sub.isDirectory()) {
                							this.setSubHeader(headerFile.getFirstChild().getNodeValue());
                						}
                					}
                				}
                			}
        				}
        			}
        		}
        	}
        } catch (Exception e) {
        	
        }
    }
    
    /**
     * Reads upload settings from organization config xml
     * @param doc  document (config xml)
     * @return
     */
    public void getUploadConfig(Document doc) {
        try {
        	NodeList uploadSettings = doc.getElementsByTagName("upload");
        
        	for (int i=0; i<uploadSettings.getLength(); i++) {
        		Node item = uploadSettings.item(i);
        		if (item.getNodeType() == Node.ELEMENT_NODE) {
        			NodeList childNodes = item.getChildNodes();
        			for (int j=0; j<childNodes.getLength(); j++) {
        				Node subItem = childNodes.item(j);
        				if (subItem.getNodeName().equalsIgnoreCase("queue")) {
        					this.setQueueSize(Integer.parseInt(subItem.getAttributes().getNamedItem("size").getNodeValue()));
        				}
        			}			
        		}	
        	}
        	
        } catch(Exception e) {
        	logger.error(e);
        }
    }
    
    /**
     * Reads initial basecollection settings from organization config xml
     * @param doc  document (config xml)
     * @return
     */
    public void getIdGenerationConfig(Document doc) {
        try {
            NodeList uploadSettings = doc.getElementsByTagName("idgeneration");
        
            for (int i=0; i<uploadSettings.getLength(); i++) {
                Node item = uploadSettings.item(i);
                if (item.getNodeType() == Node.ELEMENT_NODE) {
                    NodeList childNodes = item.getChildNodes();
                    for (int j=0; j<childNodes.getLength(); j++) {
                        Node subItem = childNodes.item(j);
                        if (subItem.getNodeName().equalsIgnoreCase("basecollection")) {
                            this.setInitialBasecollectionId(Integer.parseInt(subItem.getAttributes().getNamedItem("id").getNodeValue()));
                        }
                    }           
                }   
            }
            
        } catch(Exception e) {
            logger.error(e);
        }
    }
    
    public void getFacetFieldsFromConf(Document doc) {
        
        Vector<String> facetfields = new Vector<String>();
        try {
        	NodeList facetSettings = doc.getElementsByTagName("facets");
        	
        	for (int i=0; i<facetSettings.getLength(); i++) {
        		Node item = facetSettings.item(i);
        		if (item.getNodeType() == Node.ELEMENT_NODE) {
        			NodeList childNodes = item.getChildNodes();
        			for (int j=0; j<childNodes.getLength(); j++) {
        				Node subItem = childNodes.item(j);
        				if (subItem.getNodeName().equalsIgnoreCase("facetField")) {
        					facetfields.add(subItem.getAttributes().getNamedItem("name").getNodeValue());
        				}
        			}			
        		}	
        	}
        	searchConfiguration.setFacetFields(facetfields);
        
        } catch (Exception e) {
        	logger.error(e);
        }
    }
    
    public void getFacetFieldsforAdvBrowse(Document doc) {
        
        Vector<String> facetfields = new Vector<String>();
        try {
        	NodeList facetSettings = doc.getElementsByTagName("enabledFacets");
        	
        	for (int i=0; i<facetSettings.getLength(); i++) {
        		Node item = facetSettings.item(i);
        		if (item.getNodeType() == Node.ELEMENT_NODE) {
        			NodeList childNodes = item.getChildNodes();
        			for (int j=0; j<childNodes.getLength(); j++) {
        				Node subItem = childNodes.item(j);
        				if (subItem.getNodeName().equalsIgnoreCase("facetField")) {
        					facetfields.add(subItem.getAttributes().getNamedItem("name").getNodeValue());
        				}
        			}			
        		}	
        	}
        	
        	searchConfiguration.setAdvBrowseFacetFields(facetfields);
        
        } catch (Exception e) {
        	logger.error(e);
        }
    }
    
    public List<String> getAllObjects() {
        return this.formConfiguration.getAllForms();
    }
    
    public void getIngestForms(Document doc) {
        try {

            formConfiguration = new FormConfiguration();
            getElements(doc, "field", "ingest");
            // map elements to datastreams
            this.mapMetadataElements(doc);
            
            // check that
            List<String> contentmodels = Osa.fedoraManager.findAllObjects("osa-system:contentmodel-*");
            for (String cm : contentmodels) {
                String formName = cm.replace(RepositoryManager.CONTENTMODEL, "");
                if (formConfiguration.getForm(formName) == null) {
                    logger.error("Gui:getIngestForms() missing definitions for form: "+formName+" in "+doc.getDocumentURI());
                }
            }

        } catch (Exception e) {
            logger.error("failed to read: "+doc.getDocumentURI()+", "+e);
        }
    }
    
    /**
     * Read elements to the search form
     * @param xmlFile   xml file for the organization
     */
    public void getSearchForm(Document doc) {
        try {
            getElements(doc, "field", Gui.FORM_SEARCH);

        } catch (Exception e) {
            logger.error("failed to read: "+doc.getDocumentURI()+", "+e);
        }
    }
    
    public void getResultLayout(Document doc) {
    	try {
    		NodeList nodes = doc.getElementsByTagName("results");
            for (int i=0; i<nodes.getLength(); i++) {
                Node node = nodes.item(i);
                if (node.getNodeType() == Node.ELEMENT_NODE) {
                	searchResults = new ColumnResult(SearchResultConfiguration.Type.columns);
                	
                	NodeList subNodes = node.getChildNodes(); // results-tag's children
                	for (int j=0; j<subNodes.getLength(); j++) {
                		Node subNode = subNodes.item(j);
                		if (subNode.getNodeType() == Node.ELEMENT_NODE) {
                			if ("resultLayout".equalsIgnoreCase(subNode.getNodeName())) {
                				searchResults.getLayoutTypelist().add(subNode.getAttributes().getNamedItem("type").getNodeValue());
                				if (subNode.getAttributes().getNamedItem("defaultSort") != null) {
                					searchResults.setDefaultSort(subNode.getAttributes().getNamedItem("defaultSort").getNodeValue());
                				}
                				
                				NodeList layoutSubnodes = subNode.getChildNodes(); // results children's children
                            	for (int k=0; k<layoutSubnodes.getLength(); k++) {
                            		Node layoutSubnode = layoutSubnodes.item(k);
                            		if (layoutSubnode.getNodeType() == Node.ELEMENT_NODE) {
                            			NamedNodeMap attributes = layoutSubnode.getAttributes();
                                        Vector<String> headerVector = new Vector<String>();
                                        
                                        String name 	 = attributes.getNamedItem("name").getNodeValue();
                                        String headerkey = attributes.getNamedItem("headerKey").getNodeValue();
                                        String sortable  = attributes.getNamedItem("sortable").getNodeValue();
                                        String width 	 = attributes.getNamedItem("width").getNodeValue();
                                        
                                        headerVector.add(name);
                                        headerVector.add(headerkey);
                                        headerVector.add(sortable);
                                        headerVector.add(width);
                                        
                                        searchResults.getHeaderKeys().add(headerVector);
                                        searchResults.getColumnNames().add(name);
                            		}             		
                            	}
                			}
                			
                  			if ("paging".equalsIgnoreCase(subNode.getNodeName())) {
                				NodeList layoutSubnodes = subNode.getChildNodes();
                            	for (int l=0; l<layoutSubnodes.getLength(); l++) {
                            		Node layoutSubnode = layoutSubnodes.item(l);
                            		if (layoutSubnode.getNodeType() == Node.ELEMENT_NODE) {
                            		    if (layoutSubnode.getNodeName().equalsIgnoreCase("rows")) {
                            		        searchResults.setRows(Integer.parseInt(layoutSubnode.getFirstChild().getNodeValue()));
                            			}
                            			
                            		    if (layoutSubnode.getNodeName().equalsIgnoreCase("images")) {
                            				
                            			}
                              		}
                            	}
                			}
                		}
                	}
                }
            }
            
    	} catch (Exception e) {
    		logger.error("ERROR reading xml: " + e.getMessage()); 
    	}
    }
    
    /**
     * Returns text content of the tag
     * @param doc           xml document
     * @param tagName       tag name
     * @return              text content of the tag
     */
    public String getTextContent(Document doc, String tagName) {
        String retValue = null;
        NodeList nodes = doc.getElementsByTagName(tagName);
        
        for (int i=0; i<nodes.getLength(); i++) {
            Node node = nodes.item(i);
            if (node instanceof Element) {
                retValue = node.getTextContent();
            }
        }
        
        return retValue;
    }
    
    /**
     * Maps metadata elements to the datastreams
     */
    public void mapMetadataElements(Document doc) {
        try {
            NodeList nodes = null;
            NodeList metadataNodes = doc.getElementsByTagName("metadatamapping");
            for (int i=0; i<metadataNodes.getLength(); i++) {
                Node metadataNode = metadataNodes.item(i);
                if (metadataNode.getNodeType() == Node.ELEMENT_NODE) {
                    nodes = metadataNode.getChildNodes();
                }
            }
            
            for (int i=0; i<nodes.getLength(); i++) {
                Node node = nodes.item(i);
                
                String name = null;
                MetaDataElement.MetaDataType eType = null;
                // Enumerated values for field
                List<String> enumValues = null;
                
                if (node.getNodeType() == Node.ELEMENT_NODE && node.getNodeName().equals("element")) { 
                    if (node.getAttributes().getNamedItem("name") != null) {
                        name = node.getAttributes().getNamedItem("name").getNodeValue();
                    }
                    
                    // Get datastreams
                    NodeList dataStreamNodes = node.getChildNodes();
                    for (int j=0; j<dataStreamNodes.getLength(); j++) {
                        Node dsNode = dataStreamNodes.item(j);
                        
                        if (dsNode.getNodeType() == Node.ELEMENT_NODE) {
                            // check node name
                            if (dsNode.getNodeName().equalsIgnoreCase("mapping")) {
                                if (dsNode.getAttributes().getNamedItem("schema") != null) {
                                    String dataStream = dsNode.getAttributes().getNamedItem("schema").getNodeValue();
                                    String displayName = dsNode.getAttributes().getNamedItem("name").getNodeValue();
                                    
                                    MetaDataElement metaDataElement = new MetaDataElement(name, displayName, null, eType, null, null, null, dataStream);
                                    metaDataElement.setEnumValues(enumValues);
                                    
                                    // add datastream for form element in GUI
                                    mapDataStreams.put(name, metaDataElement);
                                }
                            }
                        }
                    }
                }
            }
            
        } catch (Exception e) {
            e.printStackTrace();
        }        
    }
    
	/**
	 * @return the searchConfiguration
	 */
	public SearchConfiguration getSearchConfiguration() {
		return searchConfiguration;
	}

	/**
	 * @param searchConfiguration the searchConfiguration to set
	 */
	public void setSearchConfiguration(SearchConfiguration searchConfiguration) {
		this.searchConfiguration = searchConfiguration;
	}

	public String getCssFile() {
		return cssFile;
	}

	public void setCssFile(String cssFile) {
		this.cssFile = cssFile;
	}

	public String getLogoFile() {
		return logoFile;
	}

	public void setLogoFile(String logoFile) {
		this.logoFile = logoFile;
	}
	
	/**
	 * @return the searchResults
	 */
	public SearchResultConfiguration getSearchResults() {
		return searchResults;
	}

	public void setSearchResultType() {
		if (resultType.equals("columns")) {
			this.searchResults.setType(SearchResultConfiguration.Type.columns);
		} else if (resultType.equals("grid")) {
			this.searchResults.setType(SearchResultConfiguration.Type.grid);
		} else {
			this.searchResults.setType(SearchResultConfiguration.Type.columns);
		}
	}
	
	/**
	 * @param searchResults the searchResults to set
	 */
	public void setSearchResults(SearchResultConfiguration searchResults) {
		this.searchResults = searchResults;
	}

	public String getResultType() {
		return resultType;
	}

	public void setResultType(String resultType) {
		this.resultType = resultType;
	}
	
	// TODO Refactor to somewhere else
	public int getQueueSize() {
		return queueSize;
	}

	public void setQueueSize(int queueSize) {
		this.queueSize = queueSize;
	}
	
	public int getInitialBasecollectionId() {
        return initialBasecollectionId;
    }

    public void setInitialBasecollectionId(int value) {
        this.initialBasecollectionId = value;
    }

	public String getMethod() {
		return disposeMethod;
	}

	public void setMethod(String method) {
		this.disposeMethod = method;
	}

	public boolean isDisposeChilds() {
		return disposeChilds;
	}

	public void setDisposeChilds(boolean disposeChilds) {
		this.disposeChilds = disposeChilds;
	}

	public String getLogoFileAlt() {
		return logoFileAlt;
	}

	public void setLogoFileAlt(String logoFileAlt) {
		this.logoFileAlt = logoFileAlt;
	}

	public String getMainHeader() {
		return mainHeader;
	}

	public void setMainHeader(String mainHeader) {
		this.mainHeader = mainHeader;
	}

	public String getSubHeader() {
		return subHeader;
	}

	public void setSubHeader(String subHeader) {
		this.subHeader = subHeader;
	}

	public String getAppTitle() {
		return appTitle;
	}

	public void setAppTitle(String appTitle) {
		this.appTitle = appTitle;
	}	
}
import fi.mamk.osa.auth.User;
import fi.mamk.osa.bean.CaptureBean;
import fi.mamk.osa.bean.FedoraBean;
import fi.mamk.osa.bean.MetaDataElement;
import fi.mamk.osa.bean.DataStream.State;
import fi.mamk.osa.core.DestructionPlan;
import fi.mamk.osa.core.Osa;
import fi.mamk.osa.database.mongo.MongoManager;
import fi.mamk.osa.fedora.RepositoryManager;
import fi.mamk.osa.ui.DisposalListItem;
import fi.mamk.osa.ui.Gui;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Locale;
import java.util.Vector;
import net.sourceforge.stripes.action.HandlesEvent;
import net.sourceforge.stripes.action.LocalizableMessage;
import net.sourceforge.stripes.action.Resolution;
import net.sourceforge.stripes.action.StreamingResolution;
import net.sourceforge.stripes.action.UrlBinding;
import net.sourceforge.stripes.validation.LocalizableError;
import org.apache.log4j.Logger;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
package fi.mamk.osa.stripes;




@UrlBinding("/Disposal.action")
public class DisposalAction extends OsaBaseActionBean {

    private static final Logger logger = Logger.getLogger(DisposalAction.class);
    private String pid;
    private Vector<String>    pidArray;
    private ArrayList<String> childrenPids;

    @HandlesEvent("showDisposalListDatatables")
    public Resolution showDisposalListDatatables() {
        
        StringBuilder json = new StringBuilder();
        Locale userLocale = this.getContext().getUser().getLocale();
        List<DisposalListItem> disposalItems = new ArrayList<DisposalListItem>();
        disposalItems = Osa.dbManager.get("mongo").getDisposalListItems(getUserOrganizationName());
        
        json.append("{" +
                "\"sEcho\": 1," +
                "\"iTotalRecords\": "+disposalItems.size()+"," +
                "\"iTotalDisplayRecords\": " +disposalItems.size()+",");
        
        json.append("\"aaData\": [");
        
        if (disposalItems.size() > 0) {
            for (DisposalListItem disposalItem : disposalItems) {
                json.append("[");
                // #1 col
                json.append("\"<input type='checkbox' value='"+disposalItem.getPid()+"'/>\","); 
                // #2 col
                json.append("\"<img src='img/nopreview.png' alt='img' width='80%'/>\","); 
                // #3 col
                json.append("\"<div class='datatables-td-container'>"+  
                            "<a href='Ingest.action?pid="+disposalItem.getPid()+"&view='><h3 class='datatables-header'>"+disposalItem.getObjectName()+"</h3></a>"+
                            "<div>"+
                                "<div class='displayInlineBlock'>"+
                                    "<span>"+new LocalizableMessage("disposal.type").getMessage(userLocale)+"</span><br/>"+
                                    "<span>"+new LocalizableMessage("disposal.requested.by").getMessage(userLocale)+"</span><br/>"+
                                    "<span>"+new LocalizableMessage("disposal.requested.date").getMessage(userLocale)+"</span><br/>"+
                                "</div>"+
                                "<div class='displayInlineBlock'>"+
                                    "<span>"+new LocalizableMessage("link.add."+disposalItem.getObjectType()).getMessage(userLocale)+"</span><br/>"+
                                    "<span>"+disposalItem.getDeleter()+"</span><br/>"+
                                    "<span>"+disposalItem.getDisposalDateString()+"</span><br/>"+
                                "</div>"+
                                "<img src='img/icons/silk/delete.png' "+
                                    "id='"+disposalItem.getPid()+"|"+disposalItem.getObjectName()+"' "+
                                    "class='pointerCursor cancel disposalaction' alt='[-]' "+
                                    "data-action='deleteFromDisposalList' "+
                                    "title='"+new LocalizableMessage("message.deletefromdisposallist").getMessage(userLocale)+"'/>"+
                                "<img src='img/icons/silk/cancel.png' "+
                                    "id='"+disposalItem.getPid()+"|"+disposalItem.getObjectName()+"' "+
                                    "class='pointerCursor delete disposalaction' alt='[x]' "+
                                    "data-action='deleteFromFedora' "+
                                    "title='"+new LocalizableMessage("button.delete").getMessage(userLocale)+"'/>"+
                            "</div>"+
                          "</div>\",");

                json.deleteCharAt(json.length()-1);
                json.append("],");
            }
            json.deleteCharAt(json.length()-1);
        }
        json.append("]}");
        return new StreamingResolution(MIME_JS, json.toString());
    }
    
    @HandlesEvent("deleteFromDisposalList")
    public Resolution deleteFromDisposalList() {
        
        String retValue = "false";
        String logContent = "";
        DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = new Date();
        String disposalTime = dateFormat.format(date);
        User user = this.getContext().getUser();