/*******************************************************************************
 * Copyright (c) 2002, 2008 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Initial Contributors:
 * The following IBM employees contributed to the Remote System Explorer
 * component that contains this file: David McKnight, Kushal Munir, 
 * Michael Berger, David Dykstal, Phil Coulthard, Don Yantzi, Eric Simpson, 
 * Emily Bruner, Mazen Faraj, Adrian Storisteanu, Li Ding, and Kent Hawley.
 * 
 * Contributors:
 * Martin Oberhuber (Wind River) - Fix 154874 - handle files with space or $ in the name 
 * Xuan Chen (IBM) - Fix 160768 - [refresh][dstore] Refresh on renamed node within a zip does not work; 
 * Xuan Chen (IBM) - Fix 189487 - copy and paste a folder did not work - workbench hang
 * Xuan Chen (IBM) - [189681] [dstore][linux] Refresh Folder in My Home messes up Refresh in Root
 * Xuan Chen (IBM) - [191280] [dstore] Expand fails for folder "/folk" with 3361 children
 * Kevin Doyle (IBM) - [195709] Windows Copying doesn't work when path contains space
 * Kevin Doyle (IBM) - [196211] DStore Move tries rename if that fails copy/delete
 * Xuan Chen (IBM) - [198046] [dstore] Cannot copy a folder into an archive file
 * Xuan Chen (IBM) - [191367] with supertransfer on, Drag & Drop Folder from DStore to DStore doesn't work
 * Martin Oberhuber (Wind River) - [199548] Avoid touching files on setReadOnly() if unnecessary
 * Kevin Doyle (IBM) - [191548]  Deleting Read-Only directory removes it from view and displays no error
 * Xuan Chen (IBM) - [202949] [archives] copy a folder from one connection to an archive file in a different connection does not work
 * Xuan Chen (IBM) - [160775] [api] rename (at least within a zip) blocks UI thread
 * David McKnight (IBM) - [196624] dstore miner IDs should be String constants rather than dynamic lookup
 * Xuan Chen (IBM) - [209827] Update DStore command implementation to enable cancelation of archive operations
 * Xuan Chen (IBM) - [194481] [dstore][Archive] Save Conflict After Renaming a File that is Open
 * David McKnight (IBM) - [209593] [api] add support for "file permissions" and "owner" properties for unix files
 * Johnson Ma (Wind River) - [195402] Add tar.gz archive support
 * Noriaki Takatsu (IBM)  - [220126] [dstore][api][breaking] Single process server for multiple clients
 * David McKnight     (IBM)   [224906] [dstore] changes for getting properties and doing exit due to single-process capability
 * David McKnight     (IBM)   [225507] [api][breaking] RSE dstore API leaks non-API types
 *******************************************************************************/

package org.eclipse.rse.dstore.universal.miners;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.ServerSocket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;

import org.eclipse.dstore.core.miners.Miner;
import org.eclipse.dstore.core.model.DE;
import org.eclipse.dstore.core.model.DataElement;
import org.eclipse.dstore.core.model.DataStoreResources;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.ArchiveQueryThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.ClassFileParser;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.CopyBatchThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.CopySingleThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.CreateFileThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.CreateFolderThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.DeleteThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.FileDescriptors;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.FileQueryThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.RenameThread;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.UniversalDownloadHandler;
import org.eclipse.rse.internal.dstore.universal.miners.filesystem.UniversalSearchHandler;
import org.eclipse.rse.services.clientserver.IClientServerConstants;
import org.eclipse.rse.services.clientserver.IServiceConstants;
import org.eclipse.rse.services.clientserver.PathUtility;
import org.eclipse.rse.services.clientserver.SystemFileClassifier;
import org.eclipse.rse.services.clientserver.SystemSearchString;
import org.eclipse.rse.services.clientserver.archiveutils.AbsoluteVirtualPath;
import org.eclipse.rse.services.clientserver.archiveutils.ArchiveHandlerManager;
import org.eclipse.rse.services.clientserver.archiveutils.ISystemArchiveHandler;
import org.eclipse.rse.services.clientserver.archiveutils.SystemJarHandler;
import org.eclipse.rse.services.clientserver.archiveutils.SystemTarHandler;
import org.eclipse.rse.services.clientserver.archiveutils.SystemTgzHandler;
import org.eclipse.rse.services.clientserver.archiveutils.SystemZipHandler;
import org.eclipse.rse.services.clientserver.archiveutils.VirtualChild;
import org.eclipse.rse.services.clientserver.java.ClassFileUtil;

public class UniversalFileSystemMiner extends Miner {

	private DataElement deUFSnode;

	private DataElement deUFSuploadlog;

	protected String filterString = "*"; //$NON-NLS-1$

	protected ArchiveHandlerManager _archiveHandlerManager;

	protected boolean showHidden = false;


	public static final String CLASSNAME = "UniversalFileSystemMiner"; //$NON-NLS-1$

	protected HashMap _cancellableThreads;

	private boolean _isWindows = false;

	public UniversalFileSystemMiner() {
		_cancellableThreads = new HashMap();
		_isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows"); //$NON-NLS-1$ //$NON-NLS-2$
		_archiveHandlerManager = ArchiveHandlerManager.getInstance();
		_archiveHandlerManager.setRegisteredHandler("zip", SystemZipHandler.class); //$NON-NLS-1$
		_archiveHandlerManager.setRegisteredHandler("jar", SystemJarHandler.class); //$NON-NLS-1$
		_archiveHandlerManager.setRegisteredHandler("tar.gz", SystemTgzHandler.class); //$NON-NLS-1$
		_archiveHandlerManager.setRegisteredHandler("tgz", SystemTgzHandler.class); //$NON-NLS-1$
		_archiveHandlerManager.setRegisteredHandler("tar", SystemTarHandler.class); //$NON-NLS-1$
	}

	/**
	 * @see Miner#handleCommand(DataElement)
	 */
	public DataElement handleCommand(DataElement theElement) {
		String name = getCommandName(theElement);

		
		DataElement status = getCommandStatus(theElement);
		DataElement subject = getCommandArgument(theElement, 0);
		
		UniversalServerUtilities.logInfo(getName(), name + ":" + subject, _dataStore); //$NON-NLS-1$
		
		String queryType = (String) subject.getElementProperty(DE.P_TYPE);
		boolean caseSensitive = !_isWindows;
		// TODO: test on WINDOWS!

		if (IUniversalDataStoreConstants.C_QUERY_VIEW_ALL.equals(name)) { 
			    DataElement attributes = getCommandArgument(theElement, 1);
			    if (attributes != null && attributes.getType().equals("attributes")) //$NON-NLS-1$
			    {
			        return handleQueryAll(subject, attributes, status, queryType,
							caseSensitive);
			    }
			    else
			    {
			        return handleQueryAll(subject, null, status, queryType,
						caseSensitive);
			    }
		} else if (IUniversalDataStoreConstants.C_QUERY_VIEW_FILES.equals(name)) { 
			    DataElement attributes = getCommandArgument(theElement, 1);
			    if (attributes != null && attributes.getType().equals("attributes")) //$NON-NLS-1$
			    {
			        return handleQueryFiles(subject, attributes, status, queryType,
							caseSensitive);			        
			    }
			    else
			    {
			        return handleQueryFiles(subject, null, status, queryType,
						caseSensitive);
			    }
		} else if (IUniversalDataStoreConstants.C_QUERY_VIEW_FOLDERS.equals(name)) { 
			    DataElement attributes = getCommandArgument(theElement, 1);
			    if (attributes != null && attributes.getType().equals("attributes")) //$NON-NLS-1$
			    {
			        return handleQueryFolders(subject, attributes, status, queryType,
							caseSensitive);
			    }
			    else
			    {
			        return handleQueryFolders(subject, null, status, queryType,
						caseSensitive);
			    }
		} else if (IUniversalDataStoreConstants.C_QUERY_ROOTS.equals(name)) { 
				return handleQueryRoots(subject, status);
		} else if (IUniversalDataStoreConstants.C_SEARCH.equals(name)) { 
				return handleSearch(theElement, status, queryType,
						caseSensitive);
		} else if (IUniversalDataStoreConstants.C_CANCEL.equals(name)) { 
				subject.getName();
				return handleCancel(subject, status);
		} else if (IUniversalDataStoreConstants.C_RENAME.equals(name)) { 
				return handleRename(subject, status);
		} else if (IUniversalDataStoreConstants.C_DELETE.equals(name)) { 
				return handleDelete(subject, status, true);
		} else if (IUniversalDataStoreConstants.C_DELETE_BATCH.equals(name)) { 
				return handleDeleteBatch(theElement, status);
		} else if (IUniversalDataStoreConstants.C_COPY.equals(name)) { 
				return handleCopy(subject, getCommandArgument(theElement, 1),
						getCommandArgument(theElement, 2), status);
		} else if (IUniversalDataStoreConstants.C_COPY_BATCH.equals(name)) { 
				return handleCopyBatch(subject, theElement, status);
		} else if (IUniversalDataStoreConstants.C_CREATE_FILE.equals(name)) { 
				return handleCreateFile(subject, status, queryType);
		} else if (IUniversalDataStoreConstants.C_CREATE_FOLDER.equals(name)) { 
				return handleCreateFolder(subject, status, queryType);
		} else if (IUniversalDataStoreConstants.C_SET_READONLY.equals(name)) { 
				return handleSetReadOnly(subject, status);
		} else if (IUniversalDataStoreConstants.C_SET_LASTMODIFIED.equals(name)) { 
				return handleSetLastModified(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_BASIC_PROPERTY.equals(name)) { 
				return handleQueryBasicProperty(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_CAN_WRITE_PROPERTY.equals(name)) {
				return handleQuerycanWriteProperty(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_ADVANCE_PROPERTY.equals(name)) {
				return handleQueryAdvanceProperty(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_EXISTS.equals(name)) { 
				return handleQueryExists(subject, status, queryType);
		} else if (IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT.equals(name)) { 
				return handleQueryGetRemoteObject(subject, status, queryType);
		} else if (IUniversalDataStoreConstants.C_GET_OSTYPE.equals(name)) { 
				return handleGetOSType(subject, status);
		} else if (IUniversalDataStoreConstants.C_DOWNLOAD_FILE.equals(name)) {
				return handleDownload(theElement, status);
		} else if (IUniversalDataStoreConstants.C_SYSTEM_ENCODING.equals(name)) {
				return handleQueryEncoding(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_UNUSED_PORT.equals(name)) {
				return handleQueryUnusedPort(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_CLASSNAME.equals(name)) { 
				return handleQueryClassName(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_QUALIFIED_CLASSNAME.equals(name)) {
				return handleQueryQualifiedClassName(subject, status);
		} else if (IUniversalDataStoreConstants.C_QUERY_FILE_PERMISSIONS.equals(name)) {
				return handleQueryFilePermissions(subject, status);
		} else if (IUniversalDataStoreConstants.C_SET_FILE_PERMISSIONS.equals(name)) {
				DataElement newPermissions = getCommandArgument(theElement, 1);
                return handleSetFilePermissions(subject, newPermissions, status);			
		} else {
			UniversalServerUtilities.logError(CLASSNAME,
					"Invalid query to handlecommand", null, _dataStore); //$NON-NLS-1$
		}
		return statusDone(status);
	}

	private DataElement handleCopyBatch(DataElement targetFolder, DataElement theElement, DataElement status) 
	{
		
		CopyBatchThread copyBatchThread = new CopyBatchThread(targetFolder, theElement, this, _isWindows, status);
		copyBatchThread.start();

		updateCancellableThreads(status.getParent(), copyBatchThread);
		
		return status;
	}


	
	

	/**
	 * Method to do a search.
	 */
	public DataElement handleSearch(DataElement theElement, DataElement status,
			String queryType, boolean fileNamesCaseSensitive) {
		File fileobj = null;

		DataElement subject = getCommandArgument(theElement, 0);

		// if the query type is against a folder, archive or a virtual folder,
		// we know to handle it
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_ARCHIVE_FILE_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR)) {

			fileobj = new File(subject.getAttribute(DE.A_VALUE)
					+ File.separatorChar + subject.getName());
		}
		// otherwise log error, and return as done
		else {
			UniversalServerUtilities.logError(CLASSNAME, "Invalid query type to handleSearch", null, _dataStore); //$NON-NLS-1$
			return statusDone(status);
		}
		//If the subject is a virtual folder, we could not just use check file.exists() to determine if we need
		//to continue process this request or not.
		boolean continueSearch = true;
		if (!queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR) && !fileobj.exists())
		{
			continueSearch = false;
		}
		if (continueSearch) {
			DataElement arg1 = getCommandArgument(theElement, 1);
			DataElement arg2 = getCommandArgument(theElement, 2);
			DataElement arg3 = getCommandArgument(theElement, 3);

			String textString = arg1.getType();
			boolean isCaseSensitive = Boolean.valueOf(arg1.getName()).booleanValue();
			boolean isTextRegex = Boolean.valueOf(arg1.getSource()).booleanValue();
			
			String fileNamesString = arg2.getType();
			
			boolean isFileNamesRegex = Boolean.valueOf(arg2.getName()).booleanValue();
			String classification = arg2.getSource();
			
			boolean isIncludeArchives = Boolean.valueOf(arg3.getType()).booleanValue();
			boolean isIncludeSubfolders = Boolean.valueOf(arg3.getName()).booleanValue();
//			boolean showHidden = Boolean.valueOf(arg3.getSource()).booleanValue();
			Boolean.valueOf(arg3.getSource()).booleanValue();
			
			SystemSearchString searchString = new SystemSearchString(
					textString, isCaseSensitive, isTextRegex, fileNamesString,
					isFileNamesRegex, isIncludeArchives, isIncludeSubfolders, classification);
			
			UniversalSearchHandler searchThread = new UniversalSearchHandler(
					_dataStore, this, searchString, !_isWindows, fileobj,
					status);
			
			searchThread.start();

			updateCancellableThreads(status.getParent(), searchThread);
			//return status;
		}

		return statusDone(status);
	}

	public DataElement handleCancel(DataElement subject, DataElement status) {
		ICancellableHandler thread = (ICancellableHandler) _cancellableThreads
				.get(subject);

		
		if (thread != null) {
			if (!thread.isDone()) {
				thread.cancel();
			}
		}

		// indicate status cancelled before indicating we are done
		statusCancelled(status);

		// indicate status done
		return statusDone(status);
	}
	

	/**
	 * Method to list the files and folders for a given filter.
	 */
	public DataElement handleQueryAll(DataElement subject, DataElement attributes, DataElement status,
			String queryType, boolean caseSensitive) 
	{
		boolean isArchive = false;
		String fullName = subject.getValue();
		
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR)) 
		{
			//check if it is a archive file
			if (ArchiveHandlerManager.getInstance().isArchive(new File(fullName)))
			{
				isArchive = true;
			}
			else
			{
				isArchive = ArchiveHandlerManager.isVirtual(fullName);
			}
		} 
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_ARCHIVE_FILE_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FILE_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR)) {
			isArchive = true;
		}
		if (isArchive)
		{
			return handleQueryAllArchive(subject, attributes, status, caseSensitive, false);
		}

		File fileobj = null;


		String filter = null;
		if (attributes != null)
		{
			filter = getFilterString(attributes.getAttribute(DE.A_SOURCE));
			showHidden = getShowHiddenFlag(attributes.getAttribute(DE.A_SOURCE));
		}
		else
		{
		    filter = getFilterString(subject.getAttribute(DE.A_SOURCE));
			showHidden = getShowHiddenFlag(subject.getAttribute(DE.A_SOURCE));
		}
		
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR))
			fileobj = new File(subject.getName());
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR))
			fileobj = new File(subject.getAttribute(DE.A_VALUE)
					+ File.separatorChar + subject.getName());
		else
		{
			UniversalServerUtilities.logError(CLASSNAME,
					"Invalid query type to handleQueryAll", null, _dataStore); //$NON-NLS-1$
		}

		if (fileobj != null)
		{
			if (!fileobj.exists())
			{
				subject.setAttribute(DE.A_TYPE, IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR);
				subject.setAttribute(DE.A_SOURCE, setProperties(fileobj));
				status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED_WITH_DOES_NOT_EXIST);	
				
				if (subject.getNestedSize() > 0)
				{
					List children = subject.getNestedData();
					for (int i = children.size() - 1; i >= 0; i--)
					{
						_dataStore.deleteObject(subject, (DataElement)children.get(i));
					}					
				}
				_dataStore.refresh(subject);
			}
			else
			{
				// query all files and folders for the filter
				internalQueryAll(subject, fileobj, queryType, filter,
						caseSensitive, IClientServerConstants.INCLUDE_ALL, status);
				return status; // query done in a thread so don't mark done
			}
		}
		
		return statusDone(status);
	}

	protected void internalQueryAll(DataElement subject, File fileobj,
			String queryType, String filter, boolean caseSensitive,
			int inclusion, DataElement status) {
		
		// do query on a thread
		FileQueryThread queryThread = new FileQueryThread(subject, fileobj, queryType, filter, caseSensitive, inclusion, showHidden, _isWindows, status);
		queryThread.start();		
		
		updateCancellableThreads(status.getParent(), queryThread);
	}

	private void updateCancellableThreads(DataElement command, ICancellableHandler thread)
	{
		//First Check to make sure that there are no "zombie" threads
		Iterator iter = _cancellableThreads.keySet().iterator();
		try
		{
			while (iter.hasNext())
			{
				String threadName = (String) iter.next();
				ICancellableHandler theThread = (ICancellableHandler) _cancellableThreads.get(threadName);
				if ((theThread == null) || 
						theThread.isDone() || theThread.isCancelled())
				{
					_cancellableThreads.remove(threadName);
				}
			}
		}
		catch (Exception e)
		{
			_dataStore.trace(e);
		}
		// save find thread in hashmap for retrieval during cancel
		_cancellableThreads.put(command, thread);
	}
	
	
	  /**
		    * Method to list the files for a given filter.
		    */
	public DataElement handleQueryFiles(DataElement subject, DataElement attributes, 
			DataElement status, String queryType, boolean caseSensitive) {

		File fileobj = null;

		String filter = null;
		if (attributes != null)
		{
			filter = getFilterString(attributes.getAttribute(DE.A_SOURCE));
			showHidden = getShowHiddenFlag(attributes.getAttribute(DE.A_SOURCE));
		}
		else
		{
		    filter = getFilterString(subject.getAttribute(DE.A_SOURCE));
			showHidden = getShowHiddenFlag(subject.getAttribute(DE.A_SOURCE));
		}
		
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR))
			fileobj = new File(subject.getName());
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR))
			fileobj = new File(subject.getAttribute(DE.A_VALUE)
					+ File.separatorChar + subject.getName());
		else
			UniversalServerUtilities.logError(CLASSNAME,
					"Invalid query type to handleQueryFiles", null, _dataStore); //$NON-NLS-1$


		if (!fileobj.exists())
		{
			subject.setAttribute(DE.A_TYPE, IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR);
			subject.setAttribute(DE.A_SOURCE, setProperties(fileobj));
			status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED_WITH_DOES_NOT_EXIST);	
			if (subject.getNestedSize() > 0)
			{
				List children = subject.getNestedData();
				for (int i = children.size() - 1; i >= 0; i--)
				{
					_dataStore.deleteObject(subject, (DataElement)children.get(i));
				}
			}
		}
		else
		{
			internalQueryAll(subject, fileobj, queryType, filter, caseSensitive, IClientServerConstants.INCLUDE_FILES_ONLY, status);
			return status; // query done in a thread so not marking done here
		}
		return statusDone(status);
	}

	/**
	 * Method to list the folders for a given filter.
	 */
	public DataElement handleQueryFolders(DataElement subject, DataElement attributes,
			DataElement status, String queryType, boolean caseSensitive) {
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_ARCHIVE_FILE_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FILE_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR)) {
			return handleQueryAllArchive(subject, attributes, status, caseSensitive, true);
		}

		File fileobj = null;
		String filter = null;
		
		if (attributes != null)
		{
			filter = getFilterString(attributes.getAttribute(DE.A_SOURCE));
			showHidden = getShowHiddenFlag(attributes.getAttribute(DE.A_SOURCE));
		}
		else
		{
		    filter = getFilterString(subject.getAttribute(DE.A_SOURCE));
			showHidden = getShowHiddenFlag(subject.getAttribute(DE.A_SOURCE));
		}

		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR))
			fileobj = new File(subject.getName());
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR))
			fileobj = new File(subject.getAttribute(DE.A_VALUE)
					+ File.separatorChar + subject.getName());
		else
			UniversalServerUtilities.logError(CLASSNAME,
					"Invalid query type to handleQueryFolders", null, _dataStore); //$NON-NLS-1$

		if (!fileobj.exists())
		{
			subject.setAttribute(DE.A_TYPE, IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR);
			subject.setAttribute(DE.A_SOURCE, setProperties(fileobj));
			status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED_WITH_DOES_NOT_EXIST);	
			if (subject.getNestedSize() > 0)
			{
				List children = subject.getNestedData();
				for (int i = children.size() - 1; i >= 0; i--)
				{
					_dataStore.deleteObject(subject, (DataElement)children.get(i));
				}
			}
		}
		else
		{
			internalQueryAll(subject, fileobj, queryType, filter, caseSensitive, IClientServerConstants.INCLUDE_FOLDERS_ONLY, status);
			return status; // query done in a thread so not marking done here
		}
		
		return statusDone(status);
	}

	/**
	 * Method to list the roots.
	 */
	public DataElement handleQueryRoots(DataElement subject, DataElement status) {
//		File fileobj = new File(subject.getName());
		new File(subject.getName());
		DataElement deObj = null;

		if (System.getProperty("os.name").toLowerCase().startsWith("windows")) { //$NON-NLS-1$ //$NON-NLS-2$
			String[] ALLDRIVES = { "c:\\", "d:\\", "e:\\", "f:\\", "g:\\", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
					"h:\\", "i:\\", "j:\\", "k:\\", "l:\\", "m:\\", "n:\\", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
					"o:\\", "p:\\", "q:\\", "r:\\", "s:\\", "t:\\", "u:\\", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$
					"v:\\", "w:\\", "x:\\", "y:\\", "z:\\" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
			for (int idx = 0; idx < ALLDRIVES.length; idx++) {
				File drive = new File(ALLDRIVES[idx]);
				if (drive.exists()) {
					try {
						String path = drive.getCanonicalPath();
						deObj = _dataStore.createObject(subject,
								IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR, path);
						deObj.setAttribute(DE.A_SOURCE, setProperties(drive));
						deObj.setAttribute(DE.A_NAME, ""); //$NON-NLS-1$
						deObj.setAttribute(DE.A_VALUE, path);
					} catch (IOException e) {
						return statusDone(status);
					}
				}
			}
		} else { // not windows
			File[] list = File.listRoots();

			for (int i = 0; i < list.length; ++i) {
				deObj = _dataStore.createObject(subject,
						IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR, list[i].getAbsolutePath());
				deObj.setAttribute(DE.A_SOURCE, setProperties(list[i]));
				deObj.setAttribute(DE.A_NAME, ""); //$NON-NLS-1$
				deObj.setAttribute(DE.A_VALUE, list[i].getAbsolutePath());
			}
		}
		_dataStore.refresh(subject);
		return statusDone(status);
	}

	/**
	 * Method to Delete a file or folder.
	 */
	public DataElement handleDelete(DataElement subject, DataElement status, boolean refreshDataStore) {
		DeleteThread deleteThread = new DeleteThread(subject,  this, _dataStore, false, status);
		deleteThread.start();

		updateCancellableThreads(status.getParent(), deleteThread);
		
		return status;
	}
	
	private DataElement handleDeleteBatch(DataElement theElement, DataElement status)
	{
		DeleteThread deleteThread = new DeleteThread(theElement,  this, _dataStore, true, status);
		deleteThread.start();

		updateCancellableThreads(status.getParent(), deleteThread);
		
		return status;
	}

	/**
	 * Method to Rename a file or folder.
	 */
	public DataElement handleRename(DataElement subject, DataElement status) {
		
		RenameThread renameThread = new RenameThread(subject,  this, _dataStore, status);
		renameThread.start();

		updateCancellableThreads(status.getParent(), renameThread);
		
		return status;
	}

	/**
	 * Method to create a new file.
	 */
	public DataElement handleCreateFile(DataElement subject,
			DataElement status, String queryType) {
		
		CreateFileThread createFileThread = new CreateFileThread(subject,  queryType, this, _dataStore, status);
		createFileThread.start();

		updateCancellableThreads(status.getParent(), createFileThread);
		
		return status;
	}

	/**
	 * Method to create a new folder.
	 */
	public DataElement handleCreateFolder(DataElement subject,
			DataElement status, String queryType) {
		CreateFolderThread createFolderThread = new CreateFolderThread(subject,  queryType, this, _dataStore, status);
		createFolderThread.start();

		updateCancellableThreads(status.getParent(), createFolderThread);
		
		return status;
	}

	/**
	 * Method to set ReadOnly to a file or folder.
	 */
	public DataElement handleSetReadOnly(DataElement subject, DataElement status) {

		File filename = new File(subject.getAttribute(DE.A_VALUE), subject.getAttribute(DE.A_NAME));
		
		if (!filename.exists())
		{
			filename = new File(subject.getAttribute(DE.A_VALUE));
		}
		if (!filename.exists())
			status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED_WITH_DOES_NOT_EXIST);
		else {
			try {
				String str = subject.getAttribute(DE.A_SOURCE);
				boolean readOnly = "true".equals(str); //$NON-NLS-1$
				boolean done = false;
				if (readOnly != filename.canWrite())
				{
					done = true;
				}
				else if (readOnly)
				{
					done = filename.setReadOnly();
				}
				else
				{
					// doesn't handle non-unix
					if (!_isWindows)
					{
						// make this read-write
						String[] cmd = new String[3];
						cmd[0] = "chmod"; //$NON-NLS-1$
						cmd[1] = "u+w"; //$NON-NLS-1$
						cmd[2] = filename.getAbsolutePath();
						Process p = Runtime.getRuntime().exec(cmd);
						int exitValue = p.waitFor();
						done = (exitValue == 0);
					}
					else
					{
						// windows version
						String[] cmd = new String[3];
						cmd[0] = "attrib"; //$NON-NLS-1$
						cmd[1] = "-R"; //$NON-NLS-1$
						cmd[2] = filename.getAbsolutePath();
						Process p = Runtime.getRuntime().exec(cmd);
						int exitValue = p.waitFor();
						done = (exitValue == 0);
					}
				}
				if (done) 
				{
					status.setAttribute(DE.A_SOURCE, IServiceConstants.SUCCESS);
				}
				else
				{
					status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED);
				}
				
				// update filename?				
				filename = new File(filename.getAbsolutePath());
				subject.setAttribute(DE.A_SOURCE, setProperties(filename));
				_dataStore.refresh(subject);
				
			} catch (Exception e) {
				UniversalServerUtilities.logError(CLASSNAME,
						"handleSetreadOnly", e, _dataStore); //$NON-NLS-1$
			}
		}
		_dataStore.refresh(subject);
		return statusDone(status);
	}

	/**
	 * Method to set LastModified to a file or folder.
	 */
	public DataElement handleSetLastModified(DataElement subject,
			DataElement status) 
	{	
		File filename = new File(subject.getAttribute(DE.A_VALUE), subject.getAttribute(DE.A_NAME));
		
		if (!filename.exists())
		{
			filename = new File(subject.getAttribute(DE.A_VALUE));
		}
		if (!filename.exists())
			status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED_WITH_DOES_NOT_EXIST);
		else {
			try {
				String str = subject.getAttribute(DE.A_SOURCE);
			
				long date = Long.parseLong(str);
				boolean done = filename.setLastModified(date);

				if (done) {
					status.setAttribute(DE.A_SOURCE, IServiceConstants.SUCCESS);
				} 
				else
				{
					status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED);
				}
				
				filename = new File(filename.getAbsolutePath());
				subject.setAttribute(DE.A_SOURCE, setProperties(filename));
				_dataStore.refresh(subject);
				
			} catch (Exception e) {
				UniversalServerUtilities.logError(CLASSNAME,
						"handleSetLastModified", e, _dataStore); //$NON-NLS-1$
			}
		}
		_dataStore.refresh(subject);
		return statusDone(status);
	}

	/**
	 * Method to Retrieve properties of the file or folder.
	 */
	protected DataElement handleQueryBasicProperty(DataElement subject,
			DataElement status) {
		File fileobj = new File(subject.getAttribute(DE.A_VALUE)
				+ File.separatorChar + subject.getName());
		subject.setAttribute(DE.A_SOURCE, setProperties(fileobj));
		_dataStore.refresh(subject);
		return statusDone(status);
	}

	/**
	 * Method to Retrieve canWrite property of the file or folder.
	 */
	protected DataElement handleQuerycanWriteProperty(DataElement subject,
			DataElement status) {
		File fileObj = new File(subject.getAttribute(DE.A_VALUE)
				+ File.separatorChar + subject.getName());

		String version = IServiceConstants.VERSION_1;
		StringBuffer buffer = new StringBuffer(50);
		boolean canWrite = fileObj.canWrite();

		buffer.append(version).append(IServiceConstants.TOKEN_SEPARATOR).append(canWrite);
		subject.setAttribute(DE.A_SOURCE, buffer.toString());
		_dataStore.refresh(subject);
		return statusDone(status);
	}

	/**
	 * Method to query advance properties.
	 */

	protected DataElement handleQueryAdvanceProperty(DataElement subject,
			DataElement status) {
		// log error currently there are no advance properties for Universal
		// Files
		return statusDone(status);
	}

	/**
	 * Method to query existence of the file or folder.
	 */
	protected DataElement handleQueryExists(DataElement subject,
			DataElement status, String queryType) {

		File fileobj = null;
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR)) {
			if (subject.getName().indexOf(
					ArchiveHandlerManager.VIRTUAL_SEPARATOR) > 0) {
				VirtualChild child = _archiveHandlerManager
						.getVirtualObject(subject.getName());
				if (child.exists()) {
					status.setAttribute(DE.A_SOURCE, "true"); //$NON-NLS-1$
					return statusDone(status);
				} else {
					status.setAttribute(DE.A_SOURCE, "false"); //$NON-NLS-1$
					return statusDone(status);
				}
			} else {
				fileobj = new File(subject.getName());
			}
		} else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILE_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_ARCHIVE_FILE_DESCRIPTOR))
			fileobj = new File(subject.getAttribute(DE.A_VALUE)
					+ File.separatorChar + subject.getName());
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR))
			fileobj = new File(subject.getAttribute(DE.A_VALUE));
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FILE_DESCRIPTOR)
				|| queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR)) {
			AbsoluteVirtualPath vpath = getAbsoluteVirtualPath(subject);
			ISystemArchiveHandler handler = _archiveHandlerManager
					.getRegisteredHandler(new File(vpath
							.getContainingArchiveString()));
			if (handler == null) {
				status.setAttribute(DE.A_SOURCE, "false"); //$NON-NLS-1$
				return statusDone(status);
			}
			VirtualChild child = handler.getVirtualFile(vpath.getVirtualPart(), null);
			if (child.exists()) {
				status.setAttribute(DE.A_SOURCE, "true"); //$NON-NLS-1$
				return statusDone(status);
			}

		}

		if (fileobj != null && fileobj.exists())
			status.setAttribute(DE.A_SOURCE, "true"); //$NON-NLS-1$
		else
			status.setAttribute(DE.A_SOURCE, "false"); //$NON-NLS-1$
		return statusDone(status);
	}

	/**
	 * Method to get remote object
	 */
	public DataElement handleQueryGetRemoteObject(DataElement subject,
			DataElement status, String queryType) {
		File fileobj = null;
		boolean isVirtual = false;
		boolean isFilter = false;
		String fullName = subject.getValue();
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR)) 
		{
			isFilter = true;
			isVirtual = ArchiveHandlerManager.isVirtual(fullName);
			String filterValue = subject.getValue();
			// . translates to home dir
			if (filterValue.equals("."))  //$NON-NLS-1$
			{
				if (_dataStore.getClient() != null){
					filterValue = _dataStore.getClient().getProperty("user.home"); //$NON-NLS-1$					
				}
				else {
					filterValue = System.getProperty("user.home"); //$NON-NLS-1$
				}
				subject.setAttribute(DE.A_VALUE, filterValue);
			}
			if (!isVirtual)
				fileobj = new File(filterValue);
		} 
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILE_DESCRIPTOR))
		{
			String name = subject.getName();
			String path = subject.getValue();
			fileobj = new File(path, name);	
		}
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR))
		{
			String name = subject.getName();			
			String path = subject.getValue();
			if (name.length() == 0)
			{
				fileobj = new File(path);
			}
			else
			{
				fileobj = new File(path, name);		
			}
		}
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FILE_DESCRIPTOR) || queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR))
		{
			isVirtual = true;
		}
		else {
			UniversalServerUtilities.logError(CLASSNAME,
					"Invalid query type to handleQueryGetRemoteObject", null, _dataStore); //$NON-NLS-1$
			return statusDone(status);
		}

		if (!isVirtual && fileobj != null && fileobj.exists()) {

			// Get the canonical path name so that we preserve case for Windows
			// systems.
			// Even though Windows is case insensitive, we still want to
			// preserve case
			// when we show the path as a property to the user
			try {
				fullName = fileobj.getCanonicalPath();

			} catch (IOException e) {
				return statusDone(status);
			}

			if (fileobj.isFile()) 
			{
				if (_archiveHandlerManager.isArchive(fileobj)) {
					subject.setAttribute(DE.A_TYPE,IUniversalDataStoreConstants.UNIVERSAL_ARCHIVE_FILE_DESCRIPTOR);
				} else {
					subject.setAttribute(DE.A_TYPE, IUniversalDataStoreConstants.UNIVERSAL_FILE_DESCRIPTOR);
				}
			} 
			else { // directory
				subject.setAttribute(DE.A_TYPE, IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR);
			}
			
			String name = fullName
			.substring(
					fullName.lastIndexOf(File.separatorChar) + 1,
					fullName.length());
			int lastFileSeparatorIndex = fullName.lastIndexOf(File.separatorChar);
			String path = "";  //$NON-NLS-1$
			if (-1 != lastFileSeparatorIndex)
			{
				if (0 == lastFileSeparatorIndex)
				{
					//Need to handle the case like "/backup".  Its parent is "/", not ""
					path = Character.toString(File.separatorChar);
				}
				else
				{
					path = fullName.substring(0, fullName.lastIndexOf(File.separatorChar));
				}
			}
			subject.setAttribute(DE.A_NAME, name);
			subject.setAttribute(DE.A_VALUE, path);
			
				

			// DKM - do basic property stuff here
			subject.setAttribute(DE.A_SOURCE, setProperties(fileobj));


			/*
			// classify the file too 
			if (fileobj.isFile()) {
				subject.setAttribute(DE.A_SOURCE, subject
						.getAttribute(DE.A_SOURCE)
						+ "|" + FileClassifier.classifyFile(fileobj));
			}
			*/

			status.setAttribute(DE.A_SOURCE, IServiceConstants.SUCCESS);
		} else if (isVirtual) {
			try {
				String goodFullPath = ArchiveHandlerManager
						.cleanUpVirtualPath(fullName);
				String goodFullName = null;
				if (isFilter)
				{
					goodFullName = goodFullPath;
				}
				else
				{
					goodFullName = goodFullPath + "/" + subject.getName();  //$NON-NLS-1$
				}
				AbsoluteVirtualPath avp = new AbsoluteVirtualPath(goodFullName);
				VirtualChild child = _archiveHandlerManager
						.getVirtualObject(goodFullName);
				if (child.exists()) {

					if (child.isDirectory) {
						subject.setAttribute(DE.A_TYPE,
								IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR);
						subject.setAttribute(DE.A_NAME, child.name);
						
						subject.setAttribute(DE.A_VALUE, avp
									.getContainingArchiveString()
									+ ArchiveHandlerManager.VIRTUAL_SEPARATOR
									+ child.path);
						

					} else {
						subject.setAttribute(DE.A_TYPE,
								IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FILE_DESCRIPTOR);
						String name = child.name;
						String path = avp.getContainingArchiveString();
						
							path = path
									+ ArchiveHandlerManager.VIRTUAL_SEPARATOR
									+ child.path;
						

						subject.setAttribute(DE.A_NAME, name);
						subject.setAttribute(DE.A_VALUE, path);
					}

					subject.setAttribute(DE.A_SOURCE, setProperties(child));
					status.setAttribute(DE.A_SOURCE, IServiceConstants.SUCCESS);
				} else {
					UniversalServerUtilities.logWarning(CLASSNAME,
							"object does not exist", _dataStore); //$NON-NLS-1$
					subject.setAttribute(DE.A_SOURCE, setProperties(child));
					status
							.setAttribute(DE.A_SOURCE,
									IServiceConstants.FAILED_WITH_DOES_NOT_EXIST);
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			// change the file type
			subject.setAttribute(DE.A_TYPE, IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR);
			subject.setAttribute(DE.A_SOURCE, setProperties(fileobj));
			status.setAttribute(DE.A_SOURCE, IServiceConstants.FAILED_WITH_DOES_NOT_EXIST);
		}

		_dataStore.refresh(subject);
		return statusDone(status);
	}

	protected DataElement getFileElement(DataElement subject, File file) {
		String fileName = file.getName();

		for (int i = 0; i < subject.getNestedSize(); i++) {
			DataElement child = subject.get(i);
			if (child.getName().equals(fileName)) {
				String type = subject.getType();
				boolean isfile = file.isFile();

				if (isfile) {
					return child;
				} else if (type.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR)) {
					return child;
				}
			}
		}

		return null;
	}


	
	/**
	 * Method to obtain the classificatoin string of file or folder.
	 */
	protected String getClassificationString(String s) {

		//StringTokenizer tokenizer = new StringTokenizer(s, IServiceConstants.TOKEN_SEPARATOR);
		String[] str = s.split("\\"+IServiceConstants.TOKEN_SEPARATOR); //$NON-NLS-1$
		int tokens = str.length;
		if (tokens < 10)
		    return null;

		
		return (str[10]);
	}
	/**
	 * Method to obtain the filter string of file or folder.
	 */
	protected String getFilterString(String s) {

		//StringTokenizer tokenizer = new StringTokenizer(s, IServiceConstants.TOKEN_SEPARATOR);
		String[] str = s.split("\\"+IServiceConstants.TOKEN_SEPARATOR); //$NON-NLS-1$
		int tokens = str.length;

		/*
		int tokens = tokenizer.countTokens();
		String[] str = new String[tokens];

		for (int i = 0; i < tokens; ++i) {
			str[i] = tokenizer.nextToken();
		}
		*/
		if (tokens > 1)	
		{
		    return (str[1]);
		}
		else
		{
		    System.out.println("problem with properties:"+s); //$NON-NLS-1$
		    return "*"; //$NON-NLS-1$
		}
	}

	/**
	 * Method to obtain the show Hidden flag for file or folder.
	 */
	protected boolean getShowHiddenFlag(String s) {

		//StringTokenizer tokenizer = new StringTokenizer(s, IServiceConstants.TOKEN_SEPARATOR);
		String[] str = s.split("\\"+IServiceConstants.TOKEN_SEPARATOR); //$NON-NLS-1$
		int tokens = str.length;
		/*
		int tokens = tokenizer.countTokens();
		String[] str = new String[tokens];

		for (int i = 0; i < tokens; ++i) {
			str[i] = tokenizer.nextToken();
		}
		*/
		if (tokens > 2)
		{
		    return ((new Boolean(str[2])).booleanValue());
		}
		else
		{
		    System.out.println("show hidden flag problem:"+s); //$NON-NLS-1$
		    return true;
		}
	}

	/**
	 * Method to obtain the depth for a search
	 */
	protected int getDepth(String s) 
	{
		String[] str = s.split("\\"+IServiceConstants.TOKEN_SEPARATOR); //$NON-NLS-1$
		int tokens = str.length;
	    /*
		StringTokenizer tokenizer = new StringTokenizer(s, IServiceConstants.TOKEN_SEPARATOR);

		int tokens = tokenizer.countTokens();
		*/
		if (tokens < 4) {
			return 1;
		}
/*
		String[] str = new String[tokens];

		for (int i = 0; i < tokens; ++i) {
			str[i] = tokenizer.nextToken();
		}
*/
		return ((new Integer(str[3])).intValue()); 
	}

	/**
	 * Method to download a file.
	 */
	protected DataElement handleDownload(DataElement theElement,  DataElement status) 
	{

		UniversalDownloadHandler downloadThread = new UniversalDownloadHandler(
				_dataStore, this, theElement, status);
		downloadThread.start();

		updateCancellableThreads(status.getParent(), downloadThread);
		return status;
	}

	/**
	 * Get the system encoding
	 */
	protected DataElement handleQueryEncoding(DataElement subject, DataElement status) {

		String encoding = System.getProperty("file.encoding"); //$NON-NLS-1$

		subject.setAttribute(DE.A_VALUE, encoding);
		_dataStore.refresh(subject);

		return statusDone(status);
	}
	
	/**
	 * Get an unused port number.
	 */
	protected DataElement handleQueryUnusedPort(DataElement subject, DataElement status) {
		
		int port = -1;

		// create a server socket with port 0 (i.e. use any free port)
		try {
			ServerSocket socket = new ServerSocket(0);
			port = socket.getLocalPort();
			socket.close();
		}
		catch (IOException e) {
			UniversalServerUtilities.logError(CLASSNAME, "Can not get unused port", e, _dataStore); //$NON-NLS-1$
			port = -1;
		}
		
		String portNum = String.valueOf(port);
		subject.setAttribute(DE.A_VALUE, portNum);
		_dataStore.refresh(subject);

		return statusDone(status);
	}

	/**
	 * Complete status.
	 */
	public DataElement statusDone(DataElement status) {
		status.setAttribute(DE.A_NAME, DataStoreResources.model_done);
		_dataStore.refresh(status);
		return status;
	}

	/**
	 * Cancel status.
	 */
	public DataElement statusCancelled(DataElement status) {
		status.setAttribute(DE.A_NAME, "cancelled"); //$NON-NLS-1$
		_dataStore.refresh(status);
		return status;
	}

	/**
	 * @see Miner#load()
	 */
	public void load() {
		// Create datastore tree structure for UniversalFileSystemMiner
		deUFSnode = _dataStore.createObject(_minerData, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR, "universal.node"); //$NON-NLS-1$
//		deUFStemp = _dataStore.createObject(deUFSnode, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR, "universal.temp");
		_dataStore.createObject(deUFSnode, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR, "universal.temp"); //$NON-NLS-1$
//		deUFSfilters = _dataStore.createObject(deUFSnode, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR, "universal.filters");
		_dataStore.createObject(deUFSnode, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR, "universal.filters"); //$NON-NLS-1$
		deUFSuploadlog = _dataStore.createObject(deUFSnode, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR, "universal.uploadlog"); //$NON-NLS-1$

		UniversalByteStreamHandler universalHandler = new UniversalByteStreamHandler(_dataStore, deUFSuploadlog);		
		
		//_dataStore.setByteStreamHandler(new UniversalByteStreamHandler(_dataStore, deUFSuploadlog));
		_dataStore.registerByteStreamHandler(universalHandler);
				
		_dataStore.refresh(_minerData);
		_dataStore.refresh(deUFSuploadlog);
	}

	public void finish() {
		//_archiveHandlerManager.dispose();
		super.finish();
	}

	/**
	 * Retrieve the fully qualified class name (including package) for the
	 * specified .class file. This information is required by the distributed
	 * debugger.
	 */
	protected DataElement handleQueryClassName(DataElement subject,
			DataElement status) {
		String filename = subject.getAttribute(DE.A_VALUE) + File.separatorChar
				+ subject.getName();

		try {
			ClassFileParser parser = new ClassFileParser(
					getInputStreamForFile(filename));
			String name = parser.getPackageName();
			if (name != null) {
				_dataStore.createObject(status, "qualifiedClassName", name); //$NON-NLS-1$
			} else {
				_dataStore.createObject(status, "qualifiedClassName", "null"); //$NON-NLS-1$ //$NON-NLS-2$
			}
		} catch (java.io.IOException e) {
			_dataStore.createObject(status, "qualifiedClassName", "null"); //$NON-NLS-1$ //$NON-NLS-2$
		}

		return statusDone(status);
	}

	/**
	 * Retrieve the fully qualified class name (including package) for a class
	 * file.
	 */
	protected DataElement handleQueryQualifiedClassName(DataElement subject, DataElement status) {

		// first get parent path
		String parentPath = subject.getAttribute(DE.A_VALUE);
		
		// get system separator
		String sep = File.separator;
		
		boolean isParentArchive = ArchiveHandlerManager.getInstance().isRegisteredArchive(parentPath);
		
		boolean isParentVirtual = ArchiveHandlerManager.isVirtual(parentPath);
		
		// parent is virtual folder, so make separator "/"
		if (isParentVirtual) {
			sep = "/"; //$NON-NLS-1$
		}
		
		// file path
		String filePath = null;
		
		// parent is not a virtual archive
		if (!isParentArchive) {
		
			// if parent path does not end with separator, then add it
			if (!parentPath.endsWith(sep)) {
				parentPath = parentPath + sep;
			}

			// add file name to get the file path
			filePath = parentPath + subject.getName();
		}
		// parent is an archive, so add virtual file separator, then the file name
		else {
			filePath = parentPath + ArchiveHandlerManager.VIRTUAL_SEPARATOR + subject.getName();
		}

		try {
			
			String className = null;
			
			// if parent is not an archive or a virtual folder, then file must be
			// a file
			if (!(isParentArchive || isParentVirtual)) {
				className = ClassFileUtil.getInstance().getQualifiedClassName(filePath);
			}
			// otherwise, file is a virtual file
			else {
				String classification = SystemFileClassifier.getInstance().classifyFile(filePath);
				String execJava = "executable(java:"; //$NON-NLS-1$
				
				int idx = classification.indexOf(execJava);
				
				if (idx != -1) {
					idx = idx + execJava.length(); 
					int jdx = classification.indexOf(")", idx); //$NON-NLS-1$
					
					if (jdx != -1) {
						
						if (jdx > idx) {
							className = classification.substring(idx, jdx);
						}
						else if (jdx == idx) {
							className = ""; //$NON-NLS-1$
						}
					}
				}
			}

			if (className != null) {
				_dataStore.createObject(status, IUniversalDataStoreConstants.TYPE_QUALIFIED_CLASSNAME, className);
			} else {
				_dataStore.createObject(status, IUniversalDataStoreConstants.TYPE_QUALIFIED_CLASSNAME, "null"); //$NON-NLS-1$
			}
		} catch (IOException e) {
			UniversalServerUtilities.logError(CLASSNAME,
					"I/O error occured trying to read class file " + filePath, //$NON-NLS-1$
					null, _dataStore);
			
			_dataStore.createObject(status, IUniversalDataStoreConstants.TYPE_QUALIFIED_CLASSNAME, "null"); //$NON-NLS-1$
		}

		return statusDone(status);
	}

	/**
	 * Method to retrieve the OS that the miner is running.
	 */
	public DataElement handleGetOSType(DataElement subject, DataElement status) {
		String osType = System.getProperty("os.name").toLowerCase(); //$NON-NLS-1$
		status.setAttribute(DE.A_SOURCE, osType);
		_dataStore.refresh(subject);
		return statusDone(status);
	}

	protected java.io.InputStream getInputStreamForFile(String filename)
			throws java.io.IOException {
		File file = new File(filename);
		return file.toURL().openStream();
	}

	/**
	 * @see Miner#extendSchema(DataElement)
	 */
	public void extendSchema(DataElement schemaRoot) {
//		DataElement root = _dataStore.find(schemaRoot, DE.A_NAME, DataStoreResources.model_root, 1);
		_dataStore.find(schemaRoot, DE.A_NAME, DataStoreResources.model_root, 1);
//		DataElement snode = createObjectDescriptor(schemaRoot, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR);
		createObjectDescriptor(schemaRoot, IUniversalDataStoreConstants.UNIVERSAL_NODE_DESCRIPTOR);

		DataElement tempnode = createObjectDescriptor(schemaRoot,
				IUniversalDataStoreConstants.UNIVERSAL_TEMP_DESCRIPTOR);

		// Define filesystem descriptors
		DataElement UniversalFilter = createObjectDescriptor(schemaRoot,
				IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR);
		FileDescriptors._deUniversalFileObject = createObjectDescriptor(schemaRoot,
				IUniversalDataStoreConstants.UNIVERSAL_FILE_DESCRIPTOR);
		FileDescriptors._deUniversalFolderObject = createObjectDescriptor(schemaRoot,
				IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR);
		FileDescriptors._deUniversalArchiveFileObject = createObjectDescriptor(
				schemaRoot, IUniversalDataStoreConstants.UNIVERSAL_ARCHIVE_FILE_DESCRIPTOR);
		FileDescriptors._deUniversalVirtualFileObject = createObjectDescriptor(
				schemaRoot, IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FILE_DESCRIPTOR);
		FileDescriptors._deUniversalVirtualFolderObject = createObjectDescriptor(
				schemaRoot, IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR);

		_dataStore.refresh(schemaRoot);

		// the cancellable object descriptor
		DataElement cancellable = _dataStore.find(schemaRoot, DE.A_NAME, DataStoreResources.model_Cancellable, 1);
		
		// Define command descriptors
		DataElement queryAllFilterDescriptor = createCommandDescriptor(UniversalFilter, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_ALL); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryAllFilterDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 		
		
		DataElement queryFilesFilterDescriptor = createCommandDescriptor(UniversalFilter, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_FILES); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryFilesFilterDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		
		DataElement queryFolderFilterDescriptor = createCommandDescriptor(UniversalFilter, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_FOLDERS); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryFolderFilterDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);		
		
		createCommandDescriptor(UniversalFilter, "Filter", IUniversalDataStoreConstants.C_QUERY_ROOTS); //$NON-NLS-1$ 

		
		DataElement queryAllDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_ALL); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryAllDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 		
		
		DataElement queryFilesDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_FILES); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryFilesDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 		

		DataElement queryFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_FOLDERS); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 		

		
		DataElement queryAllArchiveDescriptor = createCommandDescriptor(FileDescriptors._deUniversalArchiveFileObject, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_ALL); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryAllArchiveDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 		

		DataElement queryFilesArchiveDescriptor = createCommandDescriptor(FileDescriptors._deUniversalArchiveFileObject, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_FILES); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryFilesArchiveDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 		

		DataElement queryFolderArchiveDescriptor = createCommandDescriptor(FileDescriptors._deUniversalArchiveFileObject, "Filter", IUniversalDataStoreConstants.C_QUERY_VIEW_FOLDERS); //$NON-NLS-1$
		_dataStore.createReference(cancellable, queryFolderArchiveDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 		
		
		createCommandDescriptor(UniversalFilter, "GetOSType", IUniversalDataStoreConstants.C_GET_OSTYPE); //$NON-NLS-1$ 
		createCommandDescriptor(UniversalFilter, "Exists", IUniversalDataStoreConstants.C_QUERY_EXISTS); //$NON-NLS-1$ 
		createCommandDescriptor(UniversalFilter, "GetRemoteObject", IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT); //$NON-NLS-1$
		DataElement createNewFileFromFilterDescriptor = createCommandDescriptor(UniversalFilter, "CreateNewFile", IUniversalDataStoreConstants.C_CREATE_FILE); //$NON-NLS-1$
		_dataStore.createReference(cancellable, createNewFileFromFilterDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		DataElement createNewFolderFromFilterDescriptor = createCommandDescriptor(UniversalFilter, "CreateNewFolder", IUniversalDataStoreConstants.C_CREATE_FOLDER); //$NON-NLS-1$
		_dataStore.createReference(cancellable, createNewFolderFromFilterDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		createCommandDescriptor(UniversalFilter, "SetLastModified", IUniversalDataStoreConstants.C_SET_LASTMODIFIED); //$NON-NLS-1$


		_dataStore.createReference(FileDescriptors._deUniversalFileObject,
				FileDescriptors._deUniversalArchiveFileObject, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 
		_dataStore.createReference(FileDescriptors._deUniversalFolderObject,
				FileDescriptors._deUniversalArchiveFileObject, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 
		_dataStore.createReference(FileDescriptors._deUniversalFileObject,
				FileDescriptors._deUniversalVirtualFileObject, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 
		_dataStore.createReference(FileDescriptors._deUniversalFolderObject,
				FileDescriptors._deUniversalVirtualFolderObject, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 

		// create the search descriptor and make it cancelable
		DataElement searchDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Search", IUniversalDataStoreConstants.C_SEARCH); //$NON-NLS-1$ 
		_dataStore.createReference(cancellable, searchDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 
		

		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "GetAdvanceProperty", IUniversalDataStoreConstants.C_QUERY_ADVANCE_PROPERTY); //$NON-NLS-1$
		createCommandDescriptor(tempnode, "Filter", IUniversalDataStoreConstants.C_CREATE_TEMP); //$NON-NLS-1$ 
		//create deleteDescriptor and make it cancelable
		DataElement deleteFileDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFileObject, "Delete", IUniversalDataStoreConstants.C_DELETE); //$NON-NLS-1$
		_dataStore.createReference(cancellable, deleteFileDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create deleteBatchDescriptor and make it cancelable
		DataElement deleteBatchFileDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFileObject, "DeleteBatch", IUniversalDataStoreConstants.C_DELETE_BATCH); //$NON-NLS-1$
		_dataStore.createReference(cancellable, deleteBatchFileDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create createNewFileDescriptor and make it cancelable
		DataElement createNewFileDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFileObject, "CreateNewFile", IUniversalDataStoreConstants.C_CREATE_FILE); //$NON-NLS-1$
		_dataStore.createReference(cancellable, createNewFileDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create createNewFolderDescriptor and make it cancelable
		DataElement createNewFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFileObject, "CreateNewFolder", IUniversalDataStoreConstants.C_CREATE_FOLDER); //$NON-NLS-1$
		_dataStore.createReference(cancellable, createNewFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create renameDescriptor and make it cancelable
		DataElement renameFileDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFileObject, "Rename", IUniversalDataStoreConstants.C_RENAME); //$NON-NLS-1$
		_dataStore.createReference(cancellable, renameFileDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "SetReadOnly", IUniversalDataStoreConstants.C_SET_READONLY); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "SetLastModified", IUniversalDataStoreConstants.C_SET_LASTMODIFIED); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetAdvanceProperty", IUniversalDataStoreConstants.C_QUERY_ADVANCE_PROPERTY); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetBasicProperty", IUniversalDataStoreConstants.C_QUERY_BASIC_PROPERTY); //$NON-NLS-1$ 

		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetcanWriteProperty", IUniversalDataStoreConstants.C_QUERY_CAN_WRITE_PROPERTY); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "Exists", IUniversalDataStoreConstants.C_QUERY_EXISTS); //$NON-NLS-1$
		
		//create deleteDescriptor and make it cancelable
		DataElement deleteFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Delete", IUniversalDataStoreConstants.C_DELETE); //$NON-NLS-1$
		_dataStore.createReference(cancellable, deleteFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create deleteBatchDescriptor and make it cancelable
		DataElement deleteBatchFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "DeleteBatch", IUniversalDataStoreConstants.C_DELETE_BATCH); //$NON-NLS-1$
		_dataStore.createReference(cancellable, deleteBatchFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create renameDescriptor and make it cancelable
		DataElement renameFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Rename", IUniversalDataStoreConstants.C_RENAME); //$NON-NLS-1$
		_dataStore.createReference(cancellable, renameFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create copyDescriptor and make it cancelable
		DataElement copyFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Copy", IUniversalDataStoreConstants.C_COPY); //$NON-NLS-1$
		_dataStore.createReference(cancellable, copyFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create copyFolderBatchDescriptor and make it cancelable
		DataElement copyBatchFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "CopyBatch", IUniversalDataStoreConstants.C_COPY_BATCH); //$NON-NLS-1$
		_dataStore.createReference(cancellable, copyBatchFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "SetReadOnly", IUniversalDataStoreConstants.C_SET_READONLY); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "SetLastModified", IUniversalDataStoreConstants.C_SET_LASTMODIFIED); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "GetBasicProperty", IUniversalDataStoreConstants.C_QUERY_BASIC_PROPERTY); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "GetcanWriteProperty", IUniversalDataStoreConstants.C_QUERY_CAN_WRITE_PROPERTY); //$NON-NLS-1$

		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "Exists", IUniversalDataStoreConstants.C_QUERY_EXISTS); //$NON-NLS-1$
		//create createFolderDescriptor and make it cancelable
		DataElement createNewFileInFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "CreateNewFile", IUniversalDataStoreConstants.C_CREATE_FILE); //$NON-NLS-1$
		_dataStore.createReference(cancellable, createNewFileInFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		//create createFolderDescriptor and make it cancelable
		DataElement createNewFolderInFolderDescriptor = createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "CreateNewFolder", IUniversalDataStoreConstants.C_CREATE_FOLDER); //$NON-NLS-1$
		_dataStore.createReference(cancellable, createNewFolderInFolderDescriptor, DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by);
		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "GetOSType", IUniversalDataStoreConstants.C_GET_OSTYPE); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetOSType", IUniversalDataStoreConstants.C_GET_OSTYPE); //$NON-NLS-1$
        //make sure C_QUERY_GET_REMOTE_OBJECT command also available for file and folder objects
		createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "GetRemoteObject", IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetRemoteObject", IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalArchiveFileObject, "GetRemoteObject", IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalVirtualFileObject, "GetRemoteObject", IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT); //$NON-NLS-1$
		createCommandDescriptor(FileDescriptors._deUniversalVirtualFolderObject, "GetRemoteObject", IUniversalDataStoreConstants.C_QUERY_GET_REMOTE_OBJECT); //$NON-NLS-1$
		
		// create a download command descriptor and make it cancelable
		DataElement downloadDescriptor = createCommandDescriptor(
				FileDescriptors._deUniversalFileObject, "DownloadFile", IUniversalDataStoreConstants.C_DOWNLOAD_FILE); //$NON-NLS-1$
		_dataStore.createReference(cancellable, downloadDescriptor,
				DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 


		DataElement adownloadDescriptor = createCommandDescriptor(
				FileDescriptors._deUniversalArchiveFileObject, "DownloadFile", IUniversalDataStoreConstants.C_DOWNLOAD_FILE); //$NON-NLS-1$
		_dataStore.createReference(cancellable, adownloadDescriptor,
				DataStoreResources.model_abstracts, DataStoreResources.model_abstracted_by); 

		
		createCommandDescriptor(tempnode, "SystemEncoding", IUniversalDataStoreConstants.C_SYSTEM_ENCODING); //$NON-NLS-1$
		
		createCommandDescriptor(tempnode, "UnusedPort", IUniversalDataStoreConstants.C_QUERY_UNUSED_PORT); //$NON-NLS-1$

		// command descriptor to retrieve package name for a class file
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetQualifiedClassName", IUniversalDataStoreConstants.C_QUERY_CLASSNAME); //$NON-NLS-1$

		// command descriptor to retrieve qualified class name for class file
		createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetFullClassName", //$NON-NLS-1$
				IUniversalDataStoreConstants.C_QUERY_QUALIFIED_CLASSNAME);
		
		
		// permissions and ownership not supported on windows
		if (!_isWindows) {
			// descriptors for permissions
			createCommandDescriptor(UniversalFilter, "GetPermissions", IUniversalDataStoreConstants.C_QUERY_FILE_PERMISSIONS); //$NON-NLS-1$
			createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "GetPermissions", IUniversalDataStoreConstants.C_QUERY_FILE_PERMISSIONS); //$NON-NLS-1$
			createCommandDescriptor(FileDescriptors._deUniversalFileObject, "GetPermissions", IUniversalDataStoreConstants.C_QUERY_FILE_PERMISSIONS); //$NON-NLS-1$
			createCommandDescriptor(FileDescriptors._deUniversalArchiveFileObject, "GetPermissions",IUniversalDataStoreConstants.C_QUERY_FILE_PERMISSIONS); //$NON-NLS-1$
	
			createCommandDescriptor(UniversalFilter, "SetPermissions", IUniversalDataStoreConstants.C_SET_FILE_PERMISSIONS); //$NON-NLS-1$
			createCommandDescriptor(FileDescriptors._deUniversalFolderObject, "SetPermissions", IUniversalDataStoreConstants.C_SET_FILE_PERMISSIONS); //$NON-NLS-1$
			createCommandDescriptor(FileDescriptors._deUniversalFileObject, "SetPermissions", IUniversalDataStoreConstants.C_SET_FILE_PERMISSIONS); //$NON-NLS-1$
			createCommandDescriptor(FileDescriptors._deUniversalArchiveFileObject, "SetPermissions",IUniversalDataStoreConstants.C_SET_FILE_PERMISSIONS); //$NON-NLS-1$
		}
	}
	

	public AbsoluteVirtualPath getAbsoluteVirtualPath(DataElement subject) {
		StringBuffer path = new StringBuffer(subject.getAttribute(DE.A_VALUE));
		if (ArchiveHandlerManager.getInstance().isArchive(
				new File(path.toString()))) {
			path.append(ArchiveHandlerManager.VIRTUAL_SEPARATOR);
		} else {
			path.append('/');
		}
		path.append(subject.getName());
		return getAbsoluteVirtualPath(path.toString());
	}

	public AbsoluteVirtualPath getAbsoluteVirtualPath(String path) {
		AbsoluteVirtualPath vp = new AbsoluteVirtualPath(path);
		return vp;
	}



	public DataElement handleQueryAllArchive(DataElement subject, DataElement attributes, 
			DataElement status, boolean caseSensitive, boolean foldersOnly) 
	{
		// do query on a thread
		ArchiveQueryThread queryThread = new ArchiveQueryThread(subject, attributes, caseSensitive, foldersOnly, showHidden, _isWindows, status);
		queryThread.start();		
		
		updateCancellableThreads(status.getParent(), queryThread);
		return status; // query is in thread so not updating status here
	}



	public ISystemArchiveHandler getArchiveHandlerFor(String archivePath) {
		File file = new File(archivePath);
		return _archiveHandlerManager.getRegisteredHandler(file);
	}


	public DataElement handleCopy(DataElement targetFolder, DataElement sourceFile, DataElement nameObj, DataElement status) {
		
		CopySingleThread copySingleThread = new CopySingleThread(targetFolder, sourceFile, nameObj, this, _isWindows, status);
		copySingleThread.start();

		updateCancellableThreads(status.getParent(), copySingleThread);
		
		return status;
	}
	
	/**
	 * Method to obtain the properties of file or folder.
	 */
	public String setProperties(File fileObj, boolean doArchiveProperties) {
		String version = IServiceConstants.VERSION_1;
		StringBuffer buffer = new StringBuffer(500);
		long date = fileObj.lastModified();
		long size = fileObj.length();
		boolean hidden = fileObj.isHidden();
		boolean canWrite = fileObj.canWrite() ;
		boolean canRead = fileObj.canRead();

		// These extra properties here might cause problems for older clients,
		// ie: a IndexOutOfBounds in UniversalFileImpl.

		// DKM: defer this until later as it is bad for performacnes..
		// I think we're doing the full query on an archive by instantiating a
		// handler
		boolean isArchive = false;//ArchiveHandlerManager.getInstance().isArchive(fileObj);

		String comment;
		if (isArchive)
			comment = ArchiveHandlerManager.getInstance().getComment(fileObj);
		else
			comment = " "; //$NON-NLS-1$

		long compressedSize = size;
		String compressionMethod = " "; //$NON-NLS-1$
		double compressionRatio = 0;

		long expandedSize;
		if (isArchive)
			expandedSize = ArchiveHandlerManager.getInstance().getExpandedSize(
					fileObj);
		else
			expandedSize = size;

		buffer.append(version).append(IServiceConstants.TOKEN_SEPARATOR).append(date).append(
				IServiceConstants.TOKEN_SEPARATOR).append(size).append(IServiceConstants.TOKEN_SEPARATOR);
		buffer.append(hidden).append(IServiceConstants.TOKEN_SEPARATOR).append(canWrite).append(
				IServiceConstants.TOKEN_SEPARATOR).append(canRead);

		// values might not be used but we set them here just so that there are right number
		// of properties
		buffer.append(IServiceConstants.TOKEN_SEPARATOR);
		buffer.append(comment).append(IServiceConstants.TOKEN_SEPARATOR).append(compressedSize)
				.append(IServiceConstants.TOKEN_SEPARATOR).append(compressionMethod).append(
						IServiceConstants.TOKEN_SEPARATOR);
		buffer.append(compressionRatio).append(IServiceConstants.TOKEN_SEPARATOR).append(
				expandedSize);
		

		String buf = buffer.toString();
		return buf;
	}

	public String setProperties(VirtualChild fileObj) {
		String version = IServiceConstants.VERSION_1;
		StringBuffer buffer = new StringBuffer(500);
		long date = fileObj.getTimeStamp();
		long size = fileObj.getSize();
		boolean hidden = false;
		boolean canWrite = fileObj.getContainingArchive().canWrite();
		boolean canRead = fileObj.getContainingArchive().canRead();

		// These extra properties here might cause problems for older clients,
		// ie: a IndexOutOfBounds in UniversalFileImpl.
		String comment = fileObj.getComment();
		if (comment.equals("")) //$NON-NLS-1$
			comment = " "; // make sure this is still a //$NON-NLS-1$
		// token
		long compressedSize = fileObj.getCompressedSize();
		String compressionMethod = fileObj.getCompressionMethod();
		if (compressionMethod.equals("")) //$NON-NLS-1$
			compressionMethod = " "; //$NON-NLS-1$
		double compressionRatio = fileObj.getCompressionRatio();
		long expandedSize = size;

		buffer.append(version).append(IServiceConstants.TOKEN_SEPARATOR).append(date).append(
				IServiceConstants.TOKEN_SEPARATOR).append(size).append(IServiceConstants.TOKEN_SEPARATOR);
		buffer.append(hidden).append(IServiceConstants.TOKEN_SEPARATOR).append(canWrite).append(
				IServiceConstants.TOKEN_SEPARATOR).append(canRead);

		buffer.append(IServiceConstants.TOKEN_SEPARATOR);
		buffer.append(comment).append(IServiceConstants.TOKEN_SEPARATOR).append(compressedSize)
				.append(IServiceConstants.TOKEN_SEPARATOR).append(compressionMethod).append(
						IServiceConstants.TOKEN_SEPARATOR);
		buffer.append(compressionRatio).append(IServiceConstants.TOKEN_SEPARATOR).append(
				expandedSize);

		return buffer.toString();
	}
	
	public String setProperties(File fileObj) {
		return setProperties(fileObj, false);
	}


	public String getVersion()
	{
		return "7.0.0"; //$NON-NLS-1$
	}
	
	private File getFileFor(DataElement subject)
	{
		File fileobj = null;
		boolean isVirtual = false;
		String fullName = subject.getValue();
		String queryType = subject.getType();
		if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILTER_DESCRIPTOR)) 
		{
			isVirtual = ArchiveHandlerManager.isVirtual(fullName);
			String filterValue = subject.getValue();
			// . translates to home dir
			if (filterValue.equals("."))  //$NON-NLS-1$
			{
				if (_dataStore.getClient() != null){
					filterValue = _dataStore.getClient().getProperty("user.home"); //$NON-NLS-1$
				}
				else {
					filterValue = System.getProperty("user.home"); //$NON-NLS-1$
				}
				subject.setAttribute(DE.A_VALUE, filterValue);
			}
			if (!isVirtual)
				fileobj = new File(filterValue);
		} 
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FILE_DESCRIPTOR))
		{
			String name = subject.getName();
			String path = subject.getValue();
			fileobj = new File(path, name);	
		}
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_FOLDER_DESCRIPTOR))
		{
			String name = subject.getName();			
			String path = subject.getValue();
			if (name.length() == 0)
			{
				fileobj = new File(path);
			}
			else
			{
				fileobj = new File(path, name);		
			}
		}
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_ARCHIVE_FILE_DESCRIPTOR))
		{
			String name = subject.getName();			
			String path = subject.getValue();
			if (name.length() == 0)
			{
				fileobj = new File(path);
			}
			else
			{
				fileobj = new File(path, name);		
			}			
		}
		else if (queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FILE_DESCRIPTOR) || queryType.equals(IUniversalDataStoreConstants.UNIVERSAL_VIRTUAL_FOLDER_DESCRIPTOR))
		{
			isVirtual = true;
		}
		return fileobj;
	}
	
		
	/**
	 * Convert permissions in rwxrwxrwx form to octal
	 * @param userPermissions
	 * @return
	 */
	private String alphaPermissionsToOctal(String alphaPermissions)
	{
		StringBuffer buf = new StringBuffer();
		// permissions
		char[] chars = alphaPermissions.toCharArray();
		
		int offset = -1;
		for (int i = 0; i < 3; i++){
			int value = 0;
			
			if (chars[++offset] == 'r'){
				value = 4;
			}
			if (chars[++offset] == 'w'){
				value += 2;
			}
			if (chars[++offset] == 'x'){
				value += 1;
			}		
			buf.append(value);
		}
	
		return buf.toString();		
	}
	
	
	/**
	 * Gets file permissions in the form <octal permissions>|<user>|<group>
	 * @param subject
	 * @param status
	 * @return
	 */
	private DataElement handleQueryFilePermissions(DataElement subject, DataElement status)
	{
		File file = getFileFor(subject);
		
		
		// permissions in form  "drwxrwxrwx ..."
		String ldStr = simpleShellCommand("ls -ld", file); //$NON-NLS-1$
				
		StringTokenizer tokenizer = new StringTokenizer(ldStr, " \t"); //$NON-NLS-1$
										
		// permissions in form "rwxrwxrwx"
		String permString = tokenizer.nextToken().substring(1); 
		String octalPermissions = alphaPermissionsToOctal(permString);

		// user and group	
		tokenizer.nextToken(); // nothing important
		String user = tokenizer.nextToken(); // 3rd
		String group = tokenizer.nextToken(); // 4th

		String result = octalPermissions + '|' + user + '|' + group;		
        status.setAttribute(DE.A_SOURCE, result);
    	statusDone(status);
	
		return status;
	}

	/**
	 * Set file permissions including user and group
	 * @param subject
	 * @param newPermissions permissions in the form <octal permissions>|<user>|<group>
	 * @param status
	 * @return
	 */
	private DataElement handleSetFilePermissions(DataElement subject, DataElement newPermissions, DataElement status)
	{
		File file = getFileFor(subject);
		
		String permissionsStr = newPermissions.getName();
		String[] permAttributes = permissionsStr.split("\\"+IServiceConstants.TOKEN_SEPARATOR); //$NON-NLS-1$

		// set the permissions
		String result = simpleShellCommand("chmod " + permAttributes[0], file); //$NON-NLS-1$
		
		// set the user
		simpleShellCommand("chown " + permAttributes[1], file); //$NON-NLS-1$
		
		// set the group
		simpleShellCommand("chown :" + permAttributes[2], file); //$NON-NLS-1$
		
        status.setAttribute(DE.A_SOURCE, result);
    	statusDone(status);
	
		return status;
	}

	/* - not used right now so commenting out
	private String simpleShellCommand(String cmd)
	{
		String result = null;
	    String args[] = new String[3];
        args[0] = "sh"; //$NON-NLS-1$
        args[1] = "-c"; //$NON-NLS-1$
        args[2] = cmd; 
     
        BufferedReader childReader = null;
		try {
        	Process childProcess = Runtime.getRuntime().exec(args);
        	
        	childReader = new BufferedReader(new InputStreamReader(childProcess.getInputStream()));

        	result = childReader.readLine().trim();
        	childReader.close();
		}
		catch (Exception e){
			try {
				childReader.close();
			}
			catch (IOException ex){}
		}
		return result;
	        	
	}
	
	*/
	
	private String simpleShellCommand(String cmd, File file)
	{
		String result = null;
	    String args[] = new String[3];
        args[0] = "sh"; //$NON-NLS-1$
        args[1] = "-c"; //$NON-NLS-1$
        args[2] = cmd + " " + PathUtility.enQuoteUnix(file.getAbsolutePath()); //$NON-NLS-1$
     
        BufferedReader childReader = null;
		try {
        	Process childProcess = Runtime.getRuntime().exec(args);
        	
        	childReader = new BufferedReader(new InputStreamReader(childProcess.getInputStream()));

        	result = childReader.readLine().trim();
        	childReader.close();
		}
		catch (Exception e){
			try {
				childReader.close();
			}
			catch (IOException ex){}
		}
		return result;
	        	
	}
}
