public interface IDriver {
	public ISubscriberShim getSubscriber();
	/**
	 * Create a new XDS result document
	 * @return
	 */
	public XDSResultDocument newResultDoc();
	
	/**
	 * Get all the driver parameters
	 * @return
	 */
	public Map<? extends String, ? extends Parameter> getDriverParams();
	
	/**
	 * Append the source node to the given query doc
	 * @param result
	 */
	public void appendSourceInfo(WriteableDocument doc);
	
	/**
	 * Get the drivers RDN (=cn)
	 * @return
	 */
	public String getDriverInstanceName();
	
	/**
	 * Get the driver's schema
	 * @return
	 */
	public Object getSchema();
	
	/**
	 * Create a new command doc
	 * @return
	 */
	public XDSCommandDocument newCommandDoc();
	
	/**
	 * Get the association value for the given record
	 * @param record
	 * @return
	 */
	public String getAssociationField(Map<String, String> record);
	/**
	 * Get the source field for the given record
	 * @param record
	 * @return
	 */
	public String getSourceField(Map<String, String> record);
	
	/**
	 * Get a pre-configured ECMA script evaluator
	 * 
	 */
	public EcmascriptBuilder getEcmaBuilder();

} public interface IFileLocatorStrategy extends IPublisherStrategy{
	
	/**
	 * Get the next file to be processed, or null if nothing is in the queue
	 * @param trace
	 * @return
	 */
	public File[] getFileList();

} public interface IFileReadStrategy extends IPublisherStrategy{
	/**
	 * Tells the reader to prepare for reading a given file. The previous file (if any) was processed completly.
	 * @param trace
	 * @param f
	 * @throws IOException
	 */
	public void openFile(File f) throws ReadException;
    /**
     * Reads one record from the file. Return null if no more records can be read from the file.
     * @param trace
     * @return a map of key/value pairs. The key is the fieldname (String), the value is the String value for that field. Value can be null.
     * @throws IOException
     */
    public Map<String,String> readRecord() throws ReadException;
    /**
     * Tells the reader that no more records will be read from this file (readRecord returned null). 
     * The reader should release all resources associated with the file.
     * @throws IOException
     */
    public void close() throws ReadException;
    
    
	/**
	 * Get the actual schema used by the file reader. This can differ than the schema configured on the driver (eg: headers in the CSV file).
	 * @return
	 */
	public String[] getActualSchema();
} public interface IFileSorterStrategy extends IPublisherStrategy{

	/*
	 * Get the first file to process from a list of files.
	 */
	public File getFirstFile(File[] fileList);
} public interface IPubFileCleanStrategy extends IPublisherStrategy {
	/**
	 * Called before a file is processed
	 */
	public void onPreFile(File f);
	
	/**
	 * Called after a file is processed
	 */
	public void onPostFile(File f);
} public interface IPublisherLoggerStrategy extends IPublisherStrategy {
	public enum LogField{
		RECORDNUMBER, //The Number of the record
		LOGSTATUS, //RETRY, ERROR, ETC
		LOGMESSAGE, //The message in the status
		LOGEVENTID //Event ID in the status message
	}
	/**
	 * Tells the logger to prepare for writing a given file.
	 * @param schema
	 * @param f
	 * @throws IOException
	 */
	public void openFile(File f, String[] schema, EnumMap<LogField, String> logFieldSchemaMap) throws WriteException;
	/**
	 * Write one record to the file.
	 * @param m A map of key/value pairs. The key is the fieldname (String), the value is the String value for that field. Value can be null.
	 * @return 
	 * @throws IOException
	 */
	public void logCommand(int recordNumber, Map<String,String> thisRecord, XDSStatusElement xdsStatusElement) throws WriteException;
	/**
	 * Tells the logger that no more records will be added to this file. 
	 * The writer should release all resources associated with the file and close the file.
	 * @throws IOException
	 */
	public File close() throws WriteException;

} public enum LogField{
	RECORDNUMBER, //The Number of the record
	LOGSTATUS, //RETRY, ERROR, ETC
	LOGMESSAGE, //The message in the status
	LOGEVENTID //Event ID in the status message
} public interface IPublisherStrategy extends IStrategy{
    /**
     * Initialize this publisher strategy
     * @param trace
     * @param driverParams
     * @throws XDSParameterException
     */
    public void init(Trace trace, Map<String,Parameter> driverParams, IPublisher publisher) throws Exception;

} public interface IShutdown {
	/**
	 * Notification of a shutdown of the driver
	 * @param trace
	 * @param reasonXML If any of error happens, this is the place to add it.
	 */
	public void onShutdown(XDSResultDocument reasonXML);

} public interface IStrategy {
	/**
	 * Get a Map of parameter definitions. Key=parameter name, value=Parameter object
	 * @param
	 * @return the map with parameter definitions
	 */
	public Map<String,Parameter> getParameterDefinitions();

} public class ReadException extends Exception {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * @param message
	 * @param cause
	 */
	public ReadException(final String message, final Exception cause) {
		super(message,cause);
	}
	/**
	 * @param cause
	 */
	public ReadException(final Exception cause) {
		super(cause);
	}

	public ReadException(final String message) {
		super(message);
	}
} public class WriteException extends Exception {
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	/**
	 * @param message
	 * @param cause
	 */
	public WriteException(String message, Exception cause) {
		super(message,cause);
	}
	/**
	 * @param cause
	 */
	public WriteException(Exception cause) {
		super(cause);
	}
} public class RegExpFileLocator extends AbstractStrategy implements IFileLocatorStrategy {

	/**
	 * A file filter that uses regexp to find the files to process. It also
	 * checks if the file is locked or not. <b>Not</b> thread safe, but only one
	 * thread is using this
	 */
	public class RegExpFileFilter implements FilenameFilter {
		public RegExpFileFilter() {
			super();
		}

		@Override
		public boolean accept(File dir, String name) {
			regExpMatcher.reset(name);
			if (regExpMatcher.matches()) {
				trace.trace("File matches regexp:" + name, TraceLevel.DEBUG);
				File actualFile = new File(dir, name);
				// We do not read directories...
				if (actualFile.isDirectory()) {
					trace.trace("File is a folder, ignored:" + name, TraceLevel.DEBUG);
					return false;
				} else {
					// File f = new File(dir, name);
					// Try to see if it locked.
					FileChannel channel = null;
					FileLock theLock = null;
					RandomAccessFile raFileStream = null;
					try {
						raFileStream = new RandomAccessFile(actualFile, "rw");
						channel = raFileStream.getChannel();
						theLock = channel.tryLock();
						if (theLock == null) {
							trace.trace("File is locked, ignored:" + name, TraceLevel.DEBUG);
							return false;
						} else {
							return true;
						}
					} catch (java.io.FileNotFoundException e) {
						// RandomAccessFile throws FileNotFound when the file is
						// locked
						trace.trace("File is locked, ignored:" + name, TraceLevel.DEBUG);
						return false;
					} catch (Exception e) {
						// No clue why this. Return false but print stack trace.
						Util.printStackTrace(trace, e);
						return false;
					} finally {
						if (theLock != null) {
							try {
								theLock.release();
							} catch (Exception eIgnore) {
							}
						}
						if (channel != null) {
							try {
								channel.close();
							} catch (Exception eIgnore) {
							}
						}
						if (raFileStream != null) {
							try {
								raFileStream.close();
							} catch (Exception eIgnore) {
							}
						}
					}
				}
			} else {
				trace.trace("File does not match regexp, ignored:" + name, TraceLevel.DEBUG);
			}
			return false;
		}
	}

	private enum Parameters implements IStrategyParameters {
		/**
		 * Source folder parameter
		 */
		DRIVER_PARAM_SOURCE_FOLDER {
			@Override
			public String getParameterName() {
				return "regExp-sourceFolder";
			}

			@Override
			public String getDefaultValue() {
				return "/opt/novell";
			}
		},
		/**
		 * Regexp file name matcher parameter
		 */
		DRIVER_PARAM_REGEXP {
			@Override
			public String getParameterName() {
				return "regExp-regExp";
			}

			@Override
			public String getDefaultValue() {
				return ".*";
			}
		};

		@Override
		public abstract String getParameterName();

		@Override
		public abstract String getDefaultValue();

		@Override
		public DataType getDataType() {
			return DataType.STRING;
		}

		@Override
		public Constraint[] getConstraints() {
			return null;
		}
	}

	/**
	 * Folder to scan for files-to-process
	 */
	private String sourceFolder;
	/**
	 * Compiled regular expression used by the file Filter
	 */
	private Matcher regExpMatcher = null;
	private Trace trace;
	private FilenameFilter filter;

	/*
	 * (non-Javadoc)
	 *
	 * @see info.vancauwenberge.filedriver.api.IFileLocator#getNextFile()
	 */
	@Override
	public File[] getFileList() {
		trace.trace("getFileList start (" + sourceFolder + ")", TraceLevel.TRACE);
		// Get the next file object.
		File f = new File(sourceFolder);
		String[] fileList = f.list(filter);
		if ((fileList != null) && (fileList.length > 0)) {
			// Transform the list of strings to a list of files
			File[] files = new File[fileList.length];
			for (int i = 0; i < fileList.length; i++) {
				String fileName = fileList[i];
				files[i] = new File(sourceFolder, fileName);
			}
			trace.trace("getFileList done", TraceLevel.TRACE);
			return files;
		} else if (fileList == null) {
			trace.trace("FileList is null. Will try again in next polling cycle.", TraceLevel.ERROR_WARN);
		}
		trace.trace("getFileList done", TraceLevel.TRACE);
		return null;
	}

	/**
	 * Return the file filter to use. Overwrite if you want to change the
	 * default RegExpFileFilter.
	 *
	 * @param trace
	 * @return
	 */
	public FilenameFilter getFileFilter() {
		FilenameFilter filter = new RegExpFileFilter();
		return filter;
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see info.vancauwenberge.filedriver.api.IFileLocator#init(com.novell.nds.
	 * dirxml.driver.XmlDocument)
	 */
	@Override
	public void init(Trace trace, Map<String, Parameter> driverParams, IPublisher publisher)
			throws XDSParameterException {
		this.trace = trace;

		sourceFolder = getStringValueFor(Parameters.DRIVER_PARAM_SOURCE_FOLDER, driverParams);
		// driverParams.get(DRIVER_PARAM_SOURCE_FOLDER).toString();
		String regExpString = getStringValueFor(Parameters.DRIVER_PARAM_REGEXP, driverParams);
		// driverParams.get(DRIVER_PARAM_REGEXP).toString();
		if ((regExpString != null) && !"".equals(regExpString)) {
			regExpMatcher = Pattern.compile(regExpString).matcher("");
		}

		if ((sourceFolder == null) | (regExpMatcher == null)) {
			throwXDSParameterException(trace, "Sourcefolder nor regexp can be null.");
		}
		trace.trace("init:" + sourceFolder + " - " + regExpMatcher);
		// Check if the folder is exsisting
		// Create if needed.
		File f = new File(sourceFolder);
		if (!f.exists()) {
			trace.trace("Creating source folder.", TraceLevel.DEBUG);
			f.mkdirs();
		}
		if (!f.isDirectory()) {
			throwXDSParameterException(trace, "Sourcefolder is not a directory.");
		}
		filter = getFileFilter();
	}

	private void throwXDSParameterException(Trace trace, String message) throws XDSParameterException {
		trace.trace(message, TraceLevel.ERROR_WARN);
		throw new XDSParameterException(message);

	}

	public static void main(String[] args) {
		Field[] fields = File.class.getFields();
		for (Field field : fields) {
			System.out.println(field.getName() + ":" + field.getType() + " - " + field.isAccessible());
		}
		try {
			System.out.println(new RegExpFileLocator().getParameterDefinitions());
		} catch (SecurityException e) {
			e.printStackTrace();
		}
	}

	@SuppressWarnings("unchecked")
	@Override
	public <E extends Enum<?> & IStrategyParameters> Class<E> getParametersEnum() {
		return (Class<E>) Parameters.class;
	}
} public class PublisherNoLogger implements IPublisherLoggerStrategy{

	public void init(Trace trace, Map<String, Parameter> driverParams, IPublisher publisher) throws Exception {
	}

	public Map<String, Parameter> getParameterDefinitions() {
		return new HashMap<String, Parameter>(0);
	}

	public File close() throws WriteException {
		return null;
	}

	public void logCommand(int recordNumber, Map<String,String> thisRecord,
			XDSStatusElement xdsStatusElement) throws WriteException {
	}

	public void openFile(File f, String[] schema,
			EnumMap<LogField, String> logFieldSchemaMap) throws WriteException {
		// TODO Auto-generated method stub
		
	}

} public class NoPubCleaningStrategy implements IPubFileCleanStrategy{

	public Map<String, Parameter> getParameterDefinitions() {
		return null;
	}

	public void init(Trace trace, Map<String, Parameter> driverParams, IPublisher publisher) throws Exception {
		
	}

	public void onPreFile(File f) {
		
	}

	public void onPostFile(File f) {
		
	}

} public class CSVFileReader extends AbstractStrategy implements IFileReadStrategy {
	private boolean useHeaderNames;
	private char seperator;
	private String encoding;
	private boolean hasHeader;
	private boolean skipEmptyLines;
	private Thread parsingThread;
	private RecordQueue queue;
	private String[] schema;
	private Trace trace;
	private CSVFileParser handler;

	protected enum Parameters implements IStrategyParameters {
		//@formatter:off
		SKIP_EMPTY_LINES("csvReader_skipEmptyLines","true",DataType.BOOLEAN),
		USE_HEADER_NAMES("csvReader_UseHeaderNames","true",DataType.BOOLEAN),
		HAS_HEADER      ("csvReader_hasHeader"     ,"true",DataType.BOOLEAN),
		FORCED_ENCODING ("csvReader_forcedEncoding",null  ,DataType.STRING),
		SEPERATOR       ("csvReader_seperator"     ,","   ,DataType.STRING);
		//@formatter:on
		private Parameters(final String name, final String defaultValue, final DataType dataType) {
			this.name = name;
			this.defaultValue = defaultValue;
			this.dataType = dataType;
		}

		private final String name;
		private final String defaultValue;
		private final DataType dataType;

		@Override
		public String getParameterName() {
			return name;
		}

		@Override
		public String getDefaultValue() {
			return defaultValue;
		}

		@Override
		public DataType getDataType() {
			return dataType;
		}

		@Override
		public Constraint[] getConstraints() {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * info.vancauwenberge.filedriver.api.IFileReader#init(com.novell.nds.dirxml
	 * .driver.Trace, java.util.Map)
	 */
	@Override
	public void init(final Trace trace, final Map<String, Parameter> driverParams, final IPublisher publisher)
			throws XDSParameterException {
		if (trace.getTraceLevel() > TraceLevel.TRACE) {
			trace.trace("CSVFileReader.init() driverParams:" + driverParams);
		}
		this.trace = trace;

		useHeaderNames = getBoolValueFor(Parameters.USE_HEADER_NAMES, driverParams);
		// driverParams.get(TAG_USE_HEADER_NAMES).toBoolean().booleanValue();
		encoding = getStringValueFor(Parameters.FORCED_ENCODING, driverParams);
		// driverParams.get(TAG_FORCED_ENCODING).toString();
		hasHeader = getBoolValueFor(Parameters.HAS_HEADER, driverParams);
		// driverParams.get(TAG_HAS_HEADER).toBoolean().booleanValue();
		skipEmptyLines = getBoolValueFor(Parameters.SKIP_EMPTY_LINES, driverParams);
		// driverParams.get(TAG_SKIP_EMPTY_LINES).toBoolean().booleanValue();
		if ("".equals(encoding)) {
			encoding = Util.getSystemDefaultEncoding();
			trace.trace("No encoding given. Using system default of " + encoding, TraceLevel.ERROR_WARN);
		}
		// Tabs and spaces in the driver config are removed by Designer, so we
		// need to use a special 'encoding' for the tab character.
		final String strSeperator = getStringValueFor(Parameters.SEPERATOR, driverParams);
		// driverParams.get(TAG_SEPERATOR).toString();
		if ("{tab}".equalsIgnoreCase(strSeperator)) {
			seperator = '\t';
		} else if ("{space}".equalsIgnoreCase(strSeperator)) {
			seperator = ' ';
		} else if ((strSeperator != null) && !"".equals(strSeperator)) {
			seperator = strSeperator.charAt(0);// This is a required field, so
												// we should have at least one
												// character
		} else {
			throw new XDSParameterException("Invalid parameter value for seperator:" + strSeperator);
		}

		schema = GenericFileDriverShim.getSchemaAsArray(driverParams);
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * info.vancauwenberge.filedriver.api.IFileReader#openFile(com.novell.nds.
	 * dirxml.driver.Trace, java.io.File)
	 */
	@Override
	public void openFile(final File f) throws ReadException {
		try {
			// Start the parser that will parse this document
			final FileInputStream fr = new FileInputStream(f);
			final Reader reader = new UnicodeReader(fr, encoding);
			handler = new CSVFileParser(seperator, schema, skipEmptyLines, hasHeader, useHeaderNames);
			handler.resetParser();
			queue = handler.getQueue();

			parsingThread = new Thread() {
				@Override
				public void run() {
					try {
						handler.doParse(reader);
					} catch (final Exception e) {
						Util.printStackTrace(trace, e);
						queue.setFinishedInError(e);
					}
				}
			};
			parsingThread.setDaemon(true);
			parsingThread.setName("CSVParser");
			parsingThread.start();
		} catch (final Exception e1) {
			trace.trace("Exception while handeling CSV document:" + e1.getMessage(), TraceLevel.ERROR_WARN);
			throw new ReadException("Exception while handeling CSV document:" + e1.getMessage(), e1);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see
	 * info.vancauwenberge.filedriver.api.IFileReader#readRecord(com.novell.nds.
	 * dirxml.driver.Trace)
	 */
	@Override
	public Map<String, String> readRecord() throws ReadException {
		try {
			return queue.getNextRecord();
		} catch (final Exception e) {
			throw new ReadException(e);
		}
	}

	/*
	 * (non-Javadoc)
	 *
	 * @see info.vancauwenberge.filedriver.api.IFileReader#close()
	 */
	@Override
	public void close() throws ReadException {
		queue = null;
		if (parsingThread.isAlive()) {
			trace.trace("WARN: parsing thread is still alive...", TraceLevel.ERROR_WARN);
			try {
				parsingThread.join();
			} catch (final InterruptedException e) {
				e.printStackTrace();
			}
		}
		// Thread is dead. Normal situation.
		parsingThread = null;

		handler = null;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <E extends Enum<?> & IStrategyParameters> Class<E> getParametersEnum() {
		return (Class<E>) Parameters.class;
	}

	@Override
	public String[] getActualSchema() {
		// The parsing is done in a seperate thread. We need to make sure that
		// at least one record is read.
		return handler.getCurrentSchema();
	}
} public class NoSortSorter implements IFileSorterStrategy{

	/* (non-Javadoc)
	 * @see info.vancauwenberge.filedriver.api.IFileSorterStrategy#getParameterDefinitions()
	 */
	public Map<String,Parameter> getParameterDefinitions() {
		return new HashMap<String,Parameter>(0);
	}

	/* (non-Javadoc)
	 * @see info.vancauwenberge.filedriver.api.IFileSorterStrategy#getFirstFile(com.novell.nds.dirxml.driver.Trace, java.io.File[])
	 */
	public File getFirstFile(File[] fileList) {
		return fileList[0];
	}

	/* (non-Javadoc)
	 * @see info.vancauwenberge.filedriver.api.IFileSorterStrategy#init(com.novell.nds.dirxml.driver.Trace, java.util.Map)
	 */
	public void init(Trace trace, Map<String,Parameter> driverParams, IPublisher publisher) throws XDSParameterException {

	}
} public abstract class QueryMatcher {

	@SuppressWarnings("unchecked")
	public static QueryMatcher getMatcher(Trace trace, XDSQueryDocument queryDoc, IDriver driver){
    	List<XDSQueryElement> queryList = queryDoc.extractQueryElements();
		XDSQueryElement queryElem = queryList.get(0);//We only support one query element...
		String associationValue = getAssociationValueFromQuery(queryElem);
		String destDNValue = queryElem.getDestDN();
		List<XDSSearchAttrElement> attributesToSearch = queryElem.extractSearchAttrElements();
		if (((associationValue != null)||(destDNValue!=null)) 
				&& (attributesToSearch != null) && (attributesToSearch.size()>0)){
			return new AndMatcher(trace, new AssociationMatcher(trace, driver,associationValue,destDNValue), new AttributeMatcher(trace, attributesToSearch));
		}
		if ((associationValue != null)||(destDNValue!=null))
			return new AssociationMatcher(trace, driver,associationValue,destDNValue);
		if ((attributesToSearch != null) && (attributesToSearch.size()>0))
			return new AttributeMatcher(trace, attributesToSearch);
		return new NullMatcher(trace);
	}
	
	private static String getAssociationValueFromQuery(XDSQueryElement queryElem) {
		XDSAssociationElement association = queryElem.extractAssociationElement();
		String associationValue = null;
		if (association != null)
			associationValue = association.extractText();
		if (associationValue != null)
			associationValue = associationValue.trim();
		if ("".equals(associationValue))
			return null;
		return associationValue;
	}

	public abstract boolean matchesRecord(Map<String,String> aRecord);

} public class GenericFileDriverShim
extends AbstractDriverShim
implements DriverShim, IDriver
{

	//this constant identifies the channel/module being traced
	//  (e.g., Skeleton Driver\Driver)
	private static final String TRACE_SUFFIX = "GenFileDrv";


	/**
	 * All driver parameters
	 * Public for testclass access
	 */
	public enum DriverParam{
		//Driver specific parameters
		/**
		 * Schema to use. Note: we cannot use the filter to derive the schema: the filter has no defined sequence.
		 */
		SCHEMA("schema","LastName,FirstName,Title,Email,WorkPhone,Fax,WirelessPhone,Description",DataType.STRING, RequiredConstraint.REQUIRED),
		/**
		 * Object class to use in the generated events.
		 */
		OBJECT_CLASS("objectClass","User",DataType.STRING,RequiredConstraint.REQUIRED),
		/**
		 * Ecmascript creating the association
		 */
		ASSOCIATION_FIELD("association-field-script",null,DataType.STRING),
		/**
		 * Include Ecmascript libraries
		 */
		INCLUDE_ECMA_LIBRARIES("include-ecma-libraries","false",DataType.BOOLEAN),
		/**
		 * Ecmascript for creating the src-dn
		 */
		SOURCE_FIELD("source-field-script",null,DataType.STRING),
		/**
		 * Handling of special characters for field names in the above Ecma scripts
		 */
		JS_PROCESS("js-processing",EcmascriptBuilder.JsProcessing.NOPROCESSING.toString(),DataType.STRING,RequiredConstraint.REQUIRED,EcmascriptBuilder.JsProcessing.values()),
		/**
		 * Replacement char for when {@link GenericFileDriverShim.DriverParam.JS_PROCESS} is configured as {@link EcmaUtil.JsProcessing.NOPROCESSING}
		 */
		JS_REPLACEMENT_CHAR("js-replacement-char","_",DataType.CHAR),
		//Authentication parameters => do we need them???
		//SERVER(DTD.TAG_SERVER,null,DataType.STRING),
		//USER(DTD.TAG_USER,null,DataType.STRING),
		//PASSWORD(DTD.TAG_PASSWORD,null,DataType.STRING)
		;

		private final String paramName;
		private final String defaultValue;
		private final List<Constraint> cons;
		private final DataType dataType;

		private DriverParam(final String paramName, final String defaultValue, final DataType type){
			this(paramName,defaultValue,type,null,null);
		}

		private DriverParam(final String paramName, final String defaultValue, final DataType type, final Constraint cons){
			this(paramName,defaultValue,type,cons,null);
		}

		private DriverParam(final String paramName, final String defaultValue, final DataType type, final Constraint cons, final Object[] enumConstraint){
			this.paramName = paramName;
			this.defaultValue = defaultValue;
			this.cons=new LinkedList<Constraint>();
			if (cons != null) {
				this.cons.add(cons);
			}
			if (enumConstraint != null){
				final EnumConstraint enumC = new EnumConstraint();
				for (final Object object : enumConstraint) {
					enumC.addLiteral(object.toString());
				}
				this.cons.add(enumC);
			}
			this.dataType = type;
		}

		public String getParamName() {
			return paramName;
		}

		public String getDefaultValue() {
			return defaultValue;
		}

		public List<Constraint> getConstraints() {
			return cons;
		}

		public DataType getDataType() {
			return dataType;
		}
	}
	//put the tag names of the parameters here
	/*    private static final String TAG_SCHEMA = "schema";
    private static final String TAG_OBJECT_CLASS = "objectClass";
    private static final String TAG_ASSOCIATION_FIELD = "association-field-script";
    private static final String TAG_SOURCE_FIELD = "source-field-script";
    private static final String TAG_JS_PROCESS = "js-processing";
    private static final String TAG_JS_REPLACEMENT_CHAR = "js-replacement-char";

    //put the default values for the parameters here
    private static final String DEFAULT_SCHEMA = "LastName,FirstName,Title,Email,WorkPhone,Fax,WirelessPhone,Description";
    private static final String DEFAULT_OBJECT_CLASS = "User";
    private static final String DEFAULT_SERVER = null;
    private static final String DEFAULT_USER = null;
    private static final String DEFAULT_PASSWORD = null;
	private static final String DEFAULT_ASSOCIATION_FIELD = null;
	private static final String DEFAULT_SOURCE_FIELD = null;
	private static final String DEFAULT_JS_PROCESS = JsProcessing.NOPROCESSING.toString();
	private static final String DEFAULT_JS_REPLACEMENT_CHAR = "_";
	 */
	//MODIFY:  put the name of your application here
	private static final String APPLICATION_NAME = "Generic File Driver";

	/**
	 *  Object implementing
	 *  <code>com.novell.nds.dirxml.driver.SubscriptionShim</code>,
	 *  which is supplied to the DirXML engine via the
	 *  <code>getSubscriptionShim()</code> method.
	 *  <p>
	 *  @see #getSubscriptionShim()
	 */
	private FileDriverSubscriptionShim subscriptionShim = null;

	/**
	 *  Object implementing
	 *  <code>com.novell.nds.dirxml.driver.PublicationShim</code>,
	 *  which is supplied to the DirXML engine via the
	 *  <code>getPublicationShim()</code> method.
	 *  <p>
	 *  @see #getPublicationShim()
	 */
	private info.vancauwenberge.filedriver.shim.FileDriverPublicationShim publicationShim = null;

	/**
	 * The object class that this driver is supposed to handle.
	 */
	private String objectClass;

	private String associationField;

	private boolean includeEcmaLibraries = false;

	private String[] schema;


	private String sourceField;

	private EcmascriptBuilder.JsProcessing jsProcessingMode;
	private String jsIdentifierReplacement;

	/*
	 * All driver parameters as received during initialization
	 */
	private Map<String,Parameter> driverParams;

	private EcmascriptBuilder ecmaBuilder;

	/**
	 *  A Java driver shim <code>must</code> have a constructor which takes
	 *  no parameters.  The DirXML engine uses this to construct the driver
	 *  object.
	 */
	public GenericFileDriverShim()
	{
		//create temporary trace object until we know what the driver's
		//  RDN is
		setDriverRDN(TRACE_ID);
	}//SkeletonDriverShim()


	/**
	 *  A non-interface method that describes the parameters
	 *  this DriverShim is expecting.
	 *  <p>
	 *  @see GenericFileDriverShim#init(XmlDocument)
	 */
	@Override
	protected Map<String,Parameter> getParameterDefs()
	{
		//The XDS.jar library automatically checks parameter data
		//types for you.  When a RequiredConstraint is added to a parameter,
		//the library will check init documents to ensure the parameter is
		//present and has a value.  When you add RangeConstraints or
		//EnumConstraints to a parameter, the library will check parameter
		//values to see if they satisfy these constraints.
		trace.trace("getInitialDriverParams start",TraceLevel.TRACE);
		final DriverParam[] parameters = DriverParam.values();

		final HashMap<String,Parameter> driverParams = new HashMap<String,Parameter>(parameters.length);
		for (final DriverParam driverParam : parameters) {
			final Parameter param = new Parameter(driverParam.getParamName(), //tag name
					driverParam.getDefaultValue(), //default value
					driverParam.getDataType()); //data type
			final List<Constraint> cons = driverParam.getConstraints();
			for (final Constraint constraint : cons) {
				param.add(constraint);
			}
			driverParams.put(param.tagName(), param);			
		}
		trace.trace("getInitialDriverParams done",TraceLevel.TRACE);
		return driverParams;
	}//setDriverParams():void


	/**
	 *  <code>init</code> will be called after a driver is instantiated to allow
	 *  it to perform any necessary initialization before event processing begins.
	 *  Typically it will perform any setup that is common to both the subscriber
	 *  and publisher channels.
	 *  <p>
	 *  @param initXML XML document that contains the driver's
	 *      initialization parameters
	 *  @return an XML document containing status messages for this
	 *      operation
	 */
	@Override
	public XmlDocument init(final XmlDocument initXML)
	{
		//MODIFY:  initialize your driver here

		//example initialization document:

		/*
        <nds dtdversion="1.1" ndsversion="8.6">
            <source>
                <product version="1.1a">DirXML</product>
                <contact>Novell, Inc.</contact>
            </source>
            <input>
                <init-params src-dn="\NEW_DELL_TREE\NOVELL\Driver Set\Skeleton Driver (Java, XDS)">
                    <authentication-info>
                        <server>server.app:400</server>
                        <user>User1</user>
                    </authentication-info>
                    <driver-options>
                <option-1 display-name="Sample String option">This is a string</option-1>
                <option-2 display-name="Sample int option (enter an integer)">10</option-2>
                <option-3 display-name="Sample boolean option (enter a boolean value)">true</option-3>
                <option-4 display-name="Sample required option (enter some value)">not null</option-4>
            </driver-options>
                </init-params>
            </input>
        </nds>
		 */

		//example result document:

		/*
        <nds dtdversion="1.1" ndsversion="8.6">
            <source>
                <product build="20021214_0304" instance="Skeleton Driver (Java, XDS)" version="1.1">DirXML Skeleton Driver (Java, XDS)</product>
                <contact>My Company Name</contact>
            </source>
            <output>
                <status level="success" type="driver-status">
                    <parameters>
                        <option-1 display-name="Sample String option">This is a string</option-1>
                        <option-2 display-name="Sample int option (enter an integer)">10</option-2>
                        <option-3 display-name="Sample boolean option (enter a boolean value)">true</option-3>
                        <option-4 display-name="Sample required option (enter some value)">not null</option-4>
                        <password display-name="password"><!-- content suppressed -->
                        </password>
                        <server display-name="server">server.app:400</server>
                        <user display-name="user">User1</user>
                    </parameters>
                </status>
            </output>
        </nds>
		 */

		trace.trace("init start", TraceLevel.TRACE);

		//create result document for reporting status to DirXML engine
		//can't add source info yet since we don't have the driver's RDN
		final XDSResultDocument result = new XDSResultDocument();
		StatusAttributes attrs;


		try
		{
			//parse initialization document
			final XDSInitDocument init = new XDSInitDocument(initXML);
			final XDSStatusElement status;


			setDriverRDN(init.rdn());
			setDriverSrcDN(init.srcDN());
			//create new trace object that uses driver's RDN
			setTrace(TRACE_SUFFIX);
			//append a <source> element now that we know the driver's RDN
			appendSourceInfo(result);

			//get driver parameters from the initialization doc
			//Field constraints are validated by XDS => XDSException
			driverParams = getParameterDefs();//getShimParameters();
			init.parameters(driverParams);

			//Get our data. Mandatory fields have been validated by XDS (RequiredConstraint.REQUIRED)
			objectClass = driverParams.get(DriverParam.OBJECT_CLASS.getParamName()).toString();
			Object param = driverParams.get(DriverParam.ASSOCIATION_FIELD.getParamName());
			if (param!= null) {
				associationField=param.toString();
			}

			param = driverParams.get(DriverParam.SOURCE_FIELD.getParamName());
			if (param!= null) {
				sourceField=param.toString();
			}
			schema = getSchemaAsArray(driverParams);

			//JS identifier processing
			param = driverParams.get(DriverParam.JS_PROCESS.getParamName());
			if (param!= null) {
				jsProcessingMode=EcmascriptBuilder.JsProcessing.valueOf(param.toString());
			}
			param = driverParams.get(DriverParam.JS_REPLACEMENT_CHAR.getParamName());
			if (param!= null) {
				jsIdentifierReplacement=param.toString();
			}

			param = driverParams.get(DriverParam.INCLUDE_ECMA_LIBRARIES.getParamName());
			if (param != null) {
				includeEcmaLibraries = Boolean.parseBoolean(param.toString());
			}

			//get the filter
			final DriverFilter driverFilter = init.driverFilter();
			trace.trace("Filter:"+driverFilter,TraceLevel.TRACE);
			if (driverFilter!=null){
				final ClassFilter classFilter = driverFilter.getClassFilter(objectClass);
				trace.trace("ClassFilter:"+classFilter,TraceLevel.TRACE);
				if (classFilter!= null) {
					trace.trace("ClassFilter:"+classFilter.toString(),TraceLevel.TRACE);
				}
			}


			//create a publisher and subscriber instance
			subscriptionShim = new FileDriverSubscriptionShim(this);
			publicationShim = new FileDriverPublicationShim(this);

			//perform any other initialization that might be required

			//append a successful <status> element to the result doc
			attrs = StatusAttributes.factory(StatusLevel.SUCCESS,
					StatusType.DRIVER_STATUS,
					null); //event-id
			status = XDSUtil.appendStatus(result, //doc to append to
					attrs, //status attribute values
					null); //description
			//append the parameter values the driver is actually using
			status.parametersAppend(driverParams);
		}//try
		catch (final Exception e) //don't want to catch Error class with Throwable
		{
			Util.printStackTrace(trace,e);
			//e instance of XDSException:
			//
			//  init document is malformed or invalid -- or --
			//  it is missing required parameters or contains
			//  illegal parameter values

			//e instance of RuntimeException:

			//  e.g., NullPointerException

			attrs = StatusAttributes.factory(StatusLevel.FATAL,
					StatusType.DRIVER_STATUS,
					null); //event-id
			XDSUtil.appendStatus(result, //doc to append to
					attrs, //status attribute values
					null, //description
					e, //exception
					XDSUtil.appendStackTrace(e), //append stack trace?
					initXML); //xml to append
		}//catch

		//return result doc w/ status to DirXML engine
		trace.trace("init done", TraceLevel.TRACE);
		return result.toXML();
	}//init(XmlDocument):XmlDocument


	/**
	 *  <code>shutdown</code> indicates to the DriverShim that
	 *  the driver is being terminated.
	 *  <p>
	 *  @param reasonXML unused
	 *  @return an XML document containing status messages for this
	 *      operation
	 */
	@Override
	public XmlDocument shutdown(final XmlDocument reasonXML)
	{
		//MODIFY:  put your shutdown code here

		//example result document:

		/*
        <nds dtdversion="1.1" ndsversion="8.6">
            <source>
                <product build="20021214_0304" instance="Skeleton Driver (Java, XDS)" version="1.1">DirXML Skeleton Driver (Java, XDS)</product>
                <contact>My Company Name</contact>
            </source>
            <output>
                <status level="success" type="driver-status"/>
            </output>
        </nds>
		 */

		trace.trace("shutdown start", TraceLevel.TRACE);

		final XDSResultDocument result = newResultDoc();
		StatusAttributes attrs;

		try
		{
			//shutdown whatever needs shutting down
			//  which includes telling the publication shim to
			//  return control back to the DirXML engine and
			//  telling the subscription shim to free resources

			if (publicationShim != null)
			{
				publicationShim.shutdown(result);
			}

			if (subscriptionShim != null)
			{
				subscriptionShim.shutdown(result);
			}

			//TODO: test to see if we already have a status element (eg: an error from the shutdown somewhere)

			//append a successful <status> element to the result doc
			attrs = StatusAttributes.factory(StatusLevel.SUCCESS,
					StatusType.DRIVER_STATUS,
					null); //event-id
			XDSUtil.appendStatus(result, //doc to append to
					attrs, //status attribute values
					null); //description
		}//try
		catch (final Exception e) //don't want to catch Error class with Throwable
		{
			//something bad happened...
			attrs = StatusAttributes.factory(StatusLevel.FATAL,
					StatusType.DRIVER_STATUS,
					null); //event-id
			XDSUtil.appendStatus(result, //do to append to
					attrs, //status attribute values
					null, //description
					e, //exception
					true, //append stack trace?
					null); //xml to append
		}//catch

		//return the result doc w/ status to DirXML engine
		trace.trace("shutdown done", TraceLevel.TRACE);
		return result.toXML();
	}//shutdown(XmlDocument):XmlDocument


	/**
	 *  <code>getSubscriptionShim</code> gets the implementation of
	 *  SubscriptionShim that will be used to process commands on behalf of
	 *  the DirXML engine.
	 *  <p>
	 *  NOTE: the returned instance will be initialized by the DirXML engine
	 *  <p>
	 *  @see FileDriverSubscriptionShim#init(XmlDocument)
	 *  @return an instance of  SubscriptionShim that will be used to process
	 *      commands on behalf of the DirXML engine
	 */
	@Override
	public SubscriptionShim getSubscriptionShim()
	{
		trace.trace("getSubscriptionShim", TraceLevel.TRACE);

		return subscriptionShim;
	}//getSubscriptionShim():SubscriptionShim


	/**
	 *  <code>getPublicationShim</code> gets the implementation of
	 *  PublicationShim that will be invoked by the DirXML engine.
	 *  <p>
	 *  NOTE: the returned instance will be initialized by the DirXML engine
	 *  <p>
	 *  @see FileDriverPublicationShimImpl#init(XmlDocument)
	 *  @return an instance of PublicationShim that will be invoked by
	 *  the DirXML engine
	 */
	@Override
	public PublicationShim getPublicationShim()
	{
		trace.trace("getPublicationShim", TraceLevel.TRACE);

		return publicationShim;
	}//getPublicationShim():PublicationShim

	@Override
	public ISubscriberShim getSubscriber(){
		return subscriptionShim;
	}

	/**
	 *  <code>getSchema</code> returns the application schema encoded
	 *  in XDS XML.
	 *  <p>
	 *  This will be called only when the driver is not running. In other
	 *  words, if this method is called init()/shutdown() will not be called
	 *  for the current instance of the DriverShim.
	 *  <p>
	 *  @param initXML XML document containing the driver shim
	 *      initialization parameters as well as the subscription shim and
	 *      publication shim initialization parameters.
	 *  @return XML document containing the application schema or an
	 *      error status
	 */
	@Override
	public XmlDocument getSchema(final XmlDocument initXML)
	{
		//MODIFY:  get your application schema here

		//example initialization document:

		/*
        <nds dtdversion="1.1" ndsversion="8.6">
            <source>
                <product version="1.1a">DirXML</product>
                <contact>Novell, Inc.</contact>
            </source>
            <input>
                <init-params src-dn="\NEW_DELL_TREE\NOVELL\Driver Set\Skeleton Driver (Java, XDS)">
                    <authentication-info>
                        <server>server.app:400</server>
                        <user>User1</user>
                    </authentication-info>
                    <driver-options>
                <option-1 display-name="Sample String option">This is a string</option-1>
                <option-2 display-name="Sample int option (enter an integer)">10</option-2>
                <option-3 display-name="Sample boolean option (enter a boolean value)">true</option-3>
                <option-4 display-name="Sample required option (enter some value)">not null</option-4>
            </driver-options>
                    <subscriber-options>
                <sub-1 display-name="Sample Subscriber option">String for Subscriber</sub-1>
            </subscriber-options>
                    <publisher-options>
                <pub-1 display-name="Sample Publisher option">String for Publisher</pub-1>
                <polling-interval display-name="Polling interval in seconds">10</polling-interval>
            </publisher-options>
                </init-params>
            </input>
        </nds>
		 */

		//example result document:

		/*
        <nds dtdversion="1.1" ndsversion="8.6">
            <source>
                <product build="20021214_0304" instance="Skeleton Driver (Java, XDS)" version="1.1">DirXML Skeleton Driver (Java, XDS)</product>
                <contact>My Company Name</contact>
            </source>
            <output>
                <schema-def application-name="Skeleton Application" hierarchical="true">
                    <class-def class-name="fake-class-1" container="true">
                        <attr-def attr-name="fake-attr-1" case-sensitive="true" multi-valued="true" naming="true" read-only="true" required="true" type="string"/>
                    </class-def>
                    <class-def class-name="fake-class-2" container="false">
                        <attr-def attr-name="fake-attr-2" case-sensitive="false" multi-valued="false" naming="false" read-only="false" required="false" type="int"/>
                    </class-def>
                </schema-def>
                <status level="warning" type="driver-general">
                    <description>Get schema not implemented.</description>
                </status>
            </output>
        </nds>
		 */
		trace.trace("getSchema start", TraceLevel.TRACE);

		//create result document for reporting status to DirXML engine
		//can't add source info yet since we don't have the driver's instance name
		final XDSSchemaResultDocument result = new XDSSchemaResultDocument();
		StatusAttributes attrs;

		try
		{
			final XmlDocument initResultDoc = init(initXML);
			final StatusLevel level = Util.getFirstStatus(initResultDoc);

			if (level.compareTo(StatusLevel.WARNING) < 0){
				//Oops, some error occured. Return the error.
				result.empty();
				attrs = StatusAttributes.factory(level,
						StatusType.DRIVER_STATUS,
						null); //event-id

				//Search for the description
				final String descr = Util.getFirstStatusDescription(initXML);
				XDSUtil.appendStatus(result, //doc to append to
						attrs, //status attribute values
						descr, //description
						null, //exception
						false, //append stack trace?
						initXML); //xml to append
			}
			else //Init was succesfull
			{
				/*
       			 XDSInitDocument init;

				//parse initialization document
				 init = new XDSInitDocument(initXML);

				//append a <source> element to the result document
				 setDriverRDN(init.rdn());
				 setTrace(TRACE_SUFFIX);
				 */
				appendSourceInfo(result);

				//get driver parameters from the initialization doc
				//init.parameters(driverParams);

				//MODIFY:  put your application's schema in the result document

				XDSSchemaDefElement schemaDef; //the <schema-def> element from the result doc
				XDSClassDefElement userClassDef; //a <class-def> element from the result doc
				XDSAttrDefElement attrDef; //an <attr-def> element from the result doc

				//a driver that supports a real application would do whatever is
				//necessary to create an XDS schema description document and
				//return it; for example, it might create an instance of the
				//Subscriber object and query the application schema; if the
				//application has an invariant schema, the driver might just
				//have an embedded XML document describing the schema in XDS
				//terms; this embedded document could then be returned to DirXML
				//wrapped by an XmlDocument object
				//
				//however, since we are just a skeleton, return a fake schema with
				//a warning that this method isn't really implemented

				schemaDef = result.appendSchemaDefElement();
				schemaDef.setApplicationName(APPLICATION_NAME);
				schemaDef.setHierarchical(true);
				userClassDef = schemaDef.appendClassDefElement();
				userClassDef.setClassName(objectClass);
				userClassDef.setContainer(true);
				//for (int i = 0; i < schema.length; i++) {
				for (final String aField : schema) {
					attrDef = userClassDef.appendAttrDefElement();
					attrDef.setAttrName(aField);
					attrDef.setMultiValued(false);
				}
				//Let the publisher extend the schema if needed
				publicationShim.extendSchema(userClassDef,initXML);
				attrs = StatusAttributes.factory(StatusLevel.SUCCESS,
						StatusType.DRIVER_GENERAL,
						null); //event-id
				XDSUtil.appendStatus(result, //doc to append to
						attrs,  //status attribute values
						null);  //description
			}
		}//try
		catch (final Exception e) //don't want to catch Error class with Throwable
		{
			Util.printStackTrace(trace, e);
			//e instance of XDSException:
			//
			//  init document is malformed or invalid -- or --
			//  it is missing required parameters or contains
			//  illegal parameter values

			//e instance of RuntimeException:

			//  e.g., NullPointerException

			//clean out partial schema
			result.empty();
			attrs = StatusAttributes.factory(StatusLevel.FATAL,
					StatusType.DRIVER_STATUS,
					null); //event-id
			XDSUtil.appendStatus(result, //doc to append to
					attrs, //status attribute values
					null, //description
					e, //exception
					XDSUtil.appendStackTrace(e), //append stack trace?
					initXML); //xml to append
		}//catch

		trace.trace("getSchema done", TraceLevel.TRACE);
		//return result doc w/ schema and status to DirXML engine
		return result.toXML();
	}//getSchema(XmlDocument):XmlDocument

	/**
	 * Upon starting of the publisher, do whatever additional initialization that is required.
	 * @param processor
	 * @throws XDSParseException
	 */
	public void onStart(final XmlCommandProcessor processor){
		//Get the linked ecma script
		try{
			final Map<Integer, String> jsLibraries;
			if (includeEcmaLibraries){
				jsLibraries = getLinkedEcmaLibraries(processor);
			}else{
				jsLibraries = Collections.emptyMap();
			}
			this.ecmaBuilder = createEcmaBuilder(processor, jsLibraries);
		}catch (final Exception e) {
			//This should not happen, but you never know.
			//IF it happens, we do not load the libraries
			Util.printStackTrace(trace, e);
			trace.trace("Driver's ecmascript libraries not loaded.", 0);
			try {
				this.ecmaBuilder = createEcmaBuilder(processor,null);
			} catch (final XDSParseException e1) {
			}
		}
	}


	/**
	 * Create an ecma builder by loading the given ecma libraries into it's scope.
	 * @param processor
	 * @param jsLibraries
	 * @return
	 * @throws XDSParseException
	 */
	@SuppressWarnings("unchecked")
	private EcmascriptBuilder createEcmaBuilder(final XmlCommandProcessor processor,
			final Map<Integer, String> jsLibraries) throws XDSParseException {

		final EcmascriptBuilder ecmaBuilder = new EcmascriptBuilder(jsProcessingMode, jsIdentifierReplacement);

		if ((jsLibraries == null) || jsLibraries.isEmpty()) {
			return ecmaBuilder;
		}

		final Set<Integer> keys = jsLibraries.keySet();

		final List<Integer> list = new ArrayList<Integer>(keys);
		java.util.Collections.sort(list);

		for (final Integer index : list) {
			//Get the content of the library
			final ArrayList<String> readAttrDetails = new ArrayList<String>();
			readAttrDetails.add("DirXML-Data");
			final XDSQueryDocument queryDetail = Util.createQueryDoc(null, jsLibraries.get(index), null, QueryScope.ENTRY, readAttrDetails, null);
			final XmlDocument responseDetail = processor.execute(queryDetail.toXML(), null);
			final XDSQueryResultDocument resultDetail = new XDSQueryResultDocument(responseDetail);
			final List<XDSInstanceElement> instancesDetail =resultDetail.extractInstanceElements();
			if ((instancesDetail != null) && (instancesDetail.size()==1)){
				final XDSInstanceElement singleton = instancesDetail.get(0);
				final List<XDSAttrElement> attrs = singleton.extractAttrElements();
				if (attrs != null){
					for (final XDSAttrElement xdsAttrElement : attrs) {
						if ("DirXML-Data".equals(xdsAttrElement.getAttrName())){
							final List<XDSValueElement> values = xdsAttrElement.extractValueElements();
							for (final XDSValueElement xdsValueElement : values) {
								byte[] decoded;
								try {
									decoded = Base64Codec.decode(xdsValueElement.extractText());
									ecmaBuilder.addSharedLibrary(new String(decoded,"UTF-8"), jsLibraries.get(index));									
								} catch (final IOException e) {
									//This should not happen, so we just log in case it does.
									trace.trace("Failed to decode the given octet string. The ECMAscript library '"+jsLibraries.get(index)+"'will not be included.",TraceLevel.ERROR_WARN);
								}

							}
						}
					}
				}
			}
		}


		return ecmaBuilder;
	}


	/**
	 * Create a map with the ECMA library references.
	 * Key = index (the list of libraries is ordered)
	 * Value = the DN to the library
	 * @param processor
	 * @return
	 * @throws XDSParseException
	 */
	@SuppressWarnings("unchecked")
	private Map<Integer, String> getLinkedEcmaLibraries(
			final XmlCommandProcessor processor) throws XDSParseException {
		final List<String> readAttr = new ArrayList<String>();
		readAttr.add("DirXML-Policies");
		final XDSQueryDocument query = Util.createQueryDoc("DirXML-Driver", getDriverSrcDN(), null, QueryScope.ENTRY, readAttr, null);
		final XmlDocument response = processor.execute(query.toXML(), null);
		final XDSQueryResultDocument result = new XDSQueryResultDocument(response);
		final List<XDSInstanceElement> instances =result.extractInstanceElements();
		//We should have exactly 1 instance
		final Map<Integer, String> jsLibraries = new HashMap<Integer, String>();
		if ((instances != null) && (instances.size()==1)){
			final XDSInstanceElement singleton = instances.get(0);
			final List<XDSAttrElement> attrs = singleton.extractAttrElements();
			if (attrs != null) {
				for (final XDSAttrElement xdsAttrElement : attrs) {
					if ("DirXML-Policies".equals(xdsAttrElement.getAttrName())){
						final List<XDSValueElement> values = xdsAttrElement.extractValueElements();
						for (final XDSValueElement xdsValueElement : values) {
							final List<XDSComponentElement> components = xdsValueElement.extractComponentElements();
							String dn=null;
							String index=null;
							String path=null;
							//Policies have 3 components: dn,level and interval
							for (final XDSComponentElement xdsComponentElement : components) {
								if(xdsComponentElement.getName().equals("level")) {
									index=xdsComponentElement.extractText();
								} else if(xdsComponentElement.getName().equals("dn")) {
									dn=xdsComponentElement.extractText();
								} else if(xdsComponentElement.getName().equals("interval")) {
									path=xdsComponentElement.extractText();
								}
							}
							if ("3".equals(path)){//"3" indicates an ecmescript library
								trace.trace("Found ECMAscript library:"+dn,TraceLevel.DEBUG);
								jsLibraries.put(Integer.parseInt(index), dn);
							}else{
								trace.trace("Non ECMAscript policy:"+dn,TraceLevel.DEBUG);								
							}
						}
					}
				}
			}
		}
		//If we found any libraries: get the content and process them
		return jsLibraries;
	}


	@Override
	public String getAssociationField(final Map<String,String> thisRecord) {
		if ((associationField != null) && !associationField.equals("")){
			trace.trace("Calculating association value using ecmascript '"+associationField+"'",TraceLevel.TRACE);
			try{
				//final Object result = EcmaUtil.evaluateEcmaWithParams(trace, jsProcessingMode, jsIdentifierReplacement, thisRecord,associationField,DriverParam.ASSOCIATION_FIELD.getParamName());
				final Object result = ecmaBuilder.evaluateEcmaWithParams(trace, thisRecord,associationField,DriverParam.ASSOCIATION_FIELD.getParamName());
				trace.trace("Calculated association: "+ result,TraceLevel.TRACE);
				String assValue = Context.toString(result);
				if (assValue != null) {
					assValue = assValue.trim();
				}
				if ("".equals(assValue)) {
					assValue=null;
				}
				return assValue;
			}catch(final Exception e){
				final StringBuilder error = new StringBuilder("Error while calculating the association using ecmascript :'");
				error.append(associationField).append("'. ").append(e.getClass().getName()).append(":").append(e.getMessage()).append(".").append("Record read:").append(thisRecord);
				final String errorStr = error.toString();
				trace.trace(errorStr, TraceLevel.ERROR_WARN);
				throw new IllegalArgumentException(errorStr, e);
			}
		}
		else{
			trace.trace("No association script set.",TraceLevel.TRACE);
			return null;
		}
	}


	@Override
	public String getSourceField(final Map<String,String> thisRecord) {
		if ((sourceField != null) && !sourceField.equals("")){
			trace.trace("Calculating src-dn value using ecmascript '"+sourceField+"'",TraceLevel.TRACE);
			try{
				//final Object result = EcmaUtil.evaluateEcmaWithParams(trace, jsProcessingMode, jsIdentifierReplacement, thisRecord,sourceField,DriverParam.SOURCE_FIELD.getParamName());
				final Object result = ecmaBuilder.evaluateEcmaWithParams(trace, thisRecord,sourceField,DriverParam.SOURCE_FIELD.getParamName());
				trace.trace("Calculated src-dn: "+ result,TraceLevel.TRACE);
				String srcValue = Context.toString(result);
				if (srcValue != null){
					srcValue = srcValue.trim().replaceAll("[+=,\\.\\\\]", "-");
					srcValue=srcValue.trim();
				}
				if ("".equals(srcValue)) {
					srcValue=null;
				}
				return srcValue;
			}catch(final Exception e){
				final StringBuilder error = new StringBuilder("Error while calculating the src-dn value using ecmascript :'");
				error.append(sourceField).append("'. ").append(e.getClass().getName()).append(":").append(e.getMessage()).append(".").append("Record read:").append(thisRecord);
				final String errorStr = error.toString();
				trace.trace(errorStr, TraceLevel.ERROR_WARN);
				throw new IllegalArgumentException(errorStr, e);
			}
		}
		else{
			trace.trace("No src-dn script set.",TraceLevel.TRACE);
			return null;
		}
	}


	@Override
	public String[] getSchema() {
		return schema;
	}


	public String getObjectClass() {
		return objectClass;
	}


	@Override
	public Map<String,Parameter> getDriverParams() {
		return driverParams;
	}

	/*
	 * Parse the schema out of the parametermap as an array of strings
	 */
	public static String[] getSchemaAsArray(final Map<String,Parameter> parammap){
		final String tempFieldNames = parammap.get(DriverParam.SCHEMA.getParamName()).toString();
		return tempFieldNames.split(","); //We always have at least 1 element (RequiredConstraint.REQUIRED)
	}

	/**
	 * Get a preconfigured ECMA script evaluator
	 */
	@Override
	public EcmascriptBuilder getEcmaBuilder() {
		return ecmaBuilder;
	}

}//class SeletonDriverShim public class Errors
{

    public static final String INVALID_DOC      = "Document is invalid.";
    public static final String NO_ASSOC         = "Missing required association.";
	public static final String FILE_READ_ERROR  = "An error occured reading the input file.";
	public static final String FILE_WRITE_ERROR = "An error occured writing the output file.";
	public static final String PROCESS_ERROR    = "An error occured while processing a record.";
	public static final String FATAL            = "Fatal error. Stopping driver.";

}//class Errors public class MetaDataManager {
	//Meta data field names
	private static final String META_RECORDNUMBER = "recordNumber";
	private static final String META_ISLASTRECORD = "isLastRecord";
	private static final String META_FILEPATH = "filePath";
	private static final String META_FILENAME = "fileName";
	private static final String META_FILESIZE = "fileSize";

	//Evaluated meta data flags
	private boolean requiresFileSize;
	private boolean requiresFileName;
	private boolean requiresFilePath;
	private boolean requiresLastRecordFlag;
	private boolean requiresRecordNumber;
	
	public MetaDataManager(String paramMetaData){
		List<String> metaData = Arrays.asList(paramMetaData.split(","));
		requiresFileSize= metaData.contains(META_FILESIZE);
		requiresFileName = metaData.contains(META_FILENAME);
		requiresFilePath = metaData.contains(META_FILEPATH);
		requiresLastRecordFlag = metaData.contains(META_ISLASTRECORD);
		requiresRecordNumber = metaData.contains(META_RECORDNUMBER);
	}
	
	/**
     * Put the initial meta data of a given file in a map and return the map.
	 * @param workFile
	 * @return map with meta data.
	 */
	public Map<String,String> getStaticMetaData(File workFile) {
		Map<String,String> metaDataMap = new HashMap<String,String>();
		if (requiresFileSize)
			metaDataMap.put(META_FILESIZE, workFile.length()+"");
		if (requiresFileName)
			metaDataMap.put(META_FILENAME, workFile.getName());
		if (requiresFilePath)
			metaDataMap.put(META_FILEPATH, workFile.getPath());
		if (requiresLastRecordFlag)
			metaDataMap.put(META_ISLASTRECORD, "false");
		return metaDataMap;
	}
	
	/**
	 * Returns the schema field names that will be added to events generated
	 * @return
	 */
	public List<String> getSchemaFields(){
		List<String> fields = new LinkedList<String>();
		if (requiresFileSize)
			fields.add(META_FILESIZE);
		if (requiresFileName)
			fields.add(META_FILENAME);
		if (requiresFilePath)
			fields.add(META_FILEPATH);
		if (requiresLastRecordFlag)
			fields.add(META_ISLASTRECORD);
		if (requiresRecordNumber)
			fields.add(META_RECORDNUMBER);
		return fields;		
	}
	
	public void addDynamicMetaData(Map<String,String> metaDataMap, Map<String,String> thisRecord, Map<String,String> nextRecord, int recordNumber) {
		//Add the meta data if required
		thisRecord.putAll(metaDataMap);
		if (this.requiresRecordNumber)
			thisRecord.put(META_RECORDNUMBER, recordNumber+"");
		if (nextRecord == null)
			if (requiresLastRecordFlag)
				thisRecord.put(META_ISLASTRECORD, "true");
	}

	@Override
	public String toString() {
		StringBuilder builder = new StringBuilder();
		builder.append("MetaDataManager [requiresFileSize=")
				.append(requiresFileSize).append(", requiresFileName=")
				.append(requiresFileName).append(", requiresFilePath=")
				.append(requiresFilePath).append(", requiresLastRecordFlag=")
				.append(requiresLastRecordFlag)
				.append(", requiresRecordNumber=").append(requiresRecordNumber)
				.append("]");
		return builder.toString();
	}



} public class TraceLevel {
	/**
	 * Exception level (0)
	 */
	public static final int EXCEPTION = 0;
	/**
	 * Error and warning messages (1)
	 */
	public static final int ERROR_WARN = 1;
	/**
	 * Debugging information (descisions taken etc)
	 */
	public static final int DEBUG = 2;
	/**
	 * TRACE level: method entry/exit
	 */
	public static final int TRACE = 3;
} public class Util {
	static final boolean supportsJava7Move;

	static {
		// Calculate supportsJava7Move using java reflection
		boolean methodFound = false;
		try {
			Class<?> c = Class.forName("java.nio.file.Files");
			Method moveMethod = c.getMethod("move", new Class[] { Class.forName("java.nio.file.Path"),
					Class.forName("java.nio.file.Path"), Class.forName("[Ljava.nio.file.CopyOption;") });
			methodFound = (moveMethod != null);
		} catch (Throwable e) {
			methodFound = false;
		}
		supportsJava7Move = methodFound;
	}

	static public StatusLevel getStatusLevel(String level) {
		if (StatusLevel.FATAL.equals(level)) {
			return StatusLevel.FATAL;
		}
		if (StatusLevel.ERROR.equals(level)) {
			return StatusLevel.ERROR;
		}
		if (StatusLevel.WARNING.equals(level)) {
			return StatusLevel.WARNING;
		}
		if (StatusLevel.SUCCESS.equals(level)) {
			return StatusLevel.SUCCESS;
		}
		if (StatusLevel.RETRY.equals(level)) {
			return StatusLevel.RETRY;
		} else {
			return null;
		}

	}

	/**
	 * Get the first statuslevel found in the given XmlDocument or null when no
	 * status level can be found.
	 *
	 * @param xmlDoc
	 * @return
	 */
	static public StatusLevel getFirstStatus(XmlDocument xmlDoc) {
		StatusLevel level = null;
		NodeList statusFields = xmlDoc.getDocument().getElementsByTagName("status");

		if (statusFields.getLength() > 0) {
			// We have at least one status element
			Node aStatus = statusFields.item(0);
			Node levelNode = aStatus.getAttributes().getNamedItem("level");
			if (levelNode != null) {
				level = getStatusLevel(levelNode.getNodeValue());
			}
		}
		return level;
	}

	/**
	 * Get the description of the first status level found in the given
	 * XmlDocument or null when no description is associated with the first
	 * status level.
	 *
	 * @param xmlDoc
	 * @return the description
	 */
	static public String getFirstStatusDescription(XmlDocument xmlDoc) {
		String descr = null;
		NodeList statusFields = xmlDoc.getDocument().getElementsByTagName("status");

		if (statusFields.getLength() > 0) {
			// We have at least one status element
			Node aStatus = statusFields.item(0);
			NodeList statusChilds = aStatus.getChildNodes();
			int size = statusChilds.getLength();
			for (int i = 0; (i < size); i++) {
				Node aChild = statusChilds.item(i);
				if (aChild.getLocalName().equals("description")) {
					descr = aChild.getFirstChild().getNodeValue();
					break;
				}

			}
		}
		return descr;
	}

	/**
	 * Get the worst found status level from the XmlDocument (or SUCCESS when no
	 * status level can be found)
	 *
	 * @param xmlDoc
	 * @return
	 */
	static public StatusLevel getWorstStatus(XmlDocument xmlDoc) {
		StatusLevel level = StatusLevel.SUCCESS;
		NodeList statusFields = xmlDoc.getDocument().getElementsByTagName("status");
		int length = statusFields.getLength();
		for (int i = 0; i < length; i++) {
			Node aStatus = statusFields.item(i);
			Node levelNode = aStatus.getAttributes().getNamedItem("level");
			if (levelNode != null) {
				StatusLevel thisLevel = getStatusLevel(levelNode.getNodeValue());
				if (thisLevel.compareTo(level) < 0) {
					level = thisLevel;
				}
			}
		}
		return level;
	}

	public static void main(String[] args) {
		System.out.println(StatusLevel.ERROR.compareTo(StatusLevel.WARNING));
		System.out.println(getSystemDefaultEncoding());
		System.out.println("supportsJava7Move:" + supportsJava7Move);
	}

	/**
	 * Prints the stacktrace of an exception to the trace object
	 *
	 * @param trace
	 * @param e
	 */
	public static void printStackTrace(Trace trace, Throwable e) {
		StringWriter sw = new StringWriter();
		PrintWriter pw = new PrintWriter(sw);
		e.printStackTrace(pw);
		pw.close();
		trace.trace(sw.toString(), TraceLevel.EXCEPTION);
	}

	public static String getSystemDefaultEncoding() {
		return (new OutputStreamWriter(new ByteArrayOutputStream())).getEncoding();
	}

	private static FileLock getFileLock(Trace trace, FileChannel fileChannel, int counter) {
		FileLock lock = null;
		try {
			lock = fileChannel.tryLock();
		} catch (IOException e1) {
			Util.printStackTrace(trace, e1);
		}

		if (lock == null) {
			if (counter <= 0) {
				trace.trace("Unable to get lock on file. Giving up.", TraceLevel.ERROR_WARN);
				return null;
			}
			trace.trace("Failed to get write lock on file before copy. Will try again in 1 seconds",
					TraceLevel.ERROR_WARN);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
			}
			return getFileLock(trace, fileChannel, counter - 1);
		}
		trace.trace("File locked.", TraceLevel.TRACE);
		return lock;
	}

	/**
	 * Copy a file from 'from' to 'to'. Returns true if the file was copied.
	 * False otherwise.
	 *
	 * @param from
	 * @param to
	 * @return
	 * @throws IOException
	 */
	private static boolean copyFile(Trace trace, File from, File to) {
		FileInputStream fis = null;
		FileOutputStream fos = null;
		FileLock lock = null;
		try {
			fis = new FileInputStream(from);
			fos = new FileOutputStream(to);
			lock = getFileLock(trace, fos.getChannel(), 10);
			if (lock == null) {
				return false;
			}

			byte[] buffer = new byte[1024];
			int noOfBytes = 0;
			// read bytes from source file and write to destination file
			while ((noOfBytes = fis.read(buffer)) != -1) {
				fos.write(buffer, 0, noOfBytes);
			}
			return true;
		} catch (Exception e) {
			trace.trace("Exception while copying the file:", TraceLevel.ERROR_WARN);
			Util.printStackTrace(trace, e);
			return false;
		} finally {
			// close the streams using close method
			try {
				if (fos != null) {
					fos.close();
				}
			} catch (Exception ioe) {
				trace.trace("Error while closing copy output stream: ", TraceLevel.ERROR_WARN);
				Util.printStackTrace(trace, ioe);
			}
			try {
				if (fis != null) {
					fis.close();
				}
			} catch (Exception ioe) {
				trace.trace("Error while closing copy input stream: ", TraceLevel.ERROR_WARN);
				Util.printStackTrace(trace, ioe);
			}
			try {
				if ((lock != null) && lock.isValid()) {
					lock.release();
				}
			} catch (Exception ioe) {
				// It should have been closed by the fos.close();
				trace.trace("Error while releasing file lock: ", TraceLevel.ERROR_WARN);
				Util.printStackTrace(trace, ioe);
			}
		}
	}

	private static boolean moveFileJava7(Trace trace, File sourceFile, File destFile) {
		try {
			// First try the Files.move as an atomic operation
			trace.trace("Moving file from " + sourceFile.getAbsolutePath() + " to " + destFile.getAbsolutePath(),
					TraceLevel.TRACE);
			Files.move(sourceFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING,
					StandardCopyOption.ATOMIC_MOVE);
			return true;
		} catch (Throwable e) {
			try {
				// Next try the Files.move as a non-atomic operation
				trace.trace(
						"Non-atomic move from " + sourceFile.getAbsolutePath() + " to " + destFile.getAbsolutePath(),
						TraceLevel.TRACE);
				Files.move(sourceFile.toPath(), destFile.toPath(), StandardCopyOption.REPLACE_EXISTING);
				return true;
			} catch (Throwable e2) {
				Util.printStackTrace(trace, e2);
				return false;
			}
		}
	}

	private static boolean moveFileJava6(Trace trace, File sourceFile, File destFile) {
		// First try the File.renameTo as a java native operation
		if (sourceFile.renameTo(destFile)) {
			trace.trace("Renamed via java native.", TraceLevel.TRACE);
			return true;
		} else {
			// Next copy/paste the file. This is the least safe method.
			trace.trace("Trying to move via copy-delete sequence.", TraceLevel.TRACE);
			if (copyFile(trace, sourceFile, destFile)) {
				if (!sourceFile.delete()) {
					trace.trace("File was copied, but failed to delete the original file.", TraceLevel.ERROR_WARN);
				}
				trace.trace("Copy-delete sequence done.", TraceLevel.TRACE);
				return true;
			} else {
				trace.trace("Failed to move or copy the file to the destiantion.", TraceLevel.ERROR_WARN);
				return false;
			}
		}
	}

	/**
	 * Move a file from sourceFile to destFile.
	 *
	 * @param sourceFile
	 * @param destFile
	 * @return
	 */
	public static boolean moveFile(Trace trace, File sourceFile, File destFile) {
		if (supportsJava7Move) {
			trace.trace("Java7 compatibvle move", TraceLevel.TRACE);
			if (!moveFileJava7(trace, sourceFile, destFile)) {
				return moveFileJava6(trace, sourceFile, destFile);
			}
			return true;
		} else {
			trace.trace("Java6 compatibvle move", TraceLevel.TRACE);
			return moveFileJava6(trace, sourceFile, destFile);
		}

		// trace.trace("Moving file from " + sourceFile.getAbsolutePath() + " to
		// " + destFile.getAbsolutePath(),
		// TraceLevel.TRACE);
		// if (sourceFile.renameTo(destFile)) {
		// trace.trace("Moved via java native.", TraceLevel.TRACE);
		// return true;
		// } else {
		// trace.trace("Trying to move via copy-delete sequence.",
		// TraceLevel.TRACE);
		// if (copyFile(trace, sourceFile, destFile)) {
		// if (!sourceFile.delete()) {
		// trace.trace("File was copied, but failed to delete the original
		// file.", TraceLevel.ERROR_WARN);
		// }
		// trace.trace("Copy-delete sequence done.", TraceLevel.TRACE);
		// return true;
		// } else {
		// trace.trace("Failed to move or copy the file to the destiantion.",
		// TraceLevel.ERROR_WARN);
		// return false;
		// }
		// }
		/*
		 * Java 7 move not yet supported on all environments. try { // first try
		 * the renameTo method trace.trace("Moving file from "
		 * +sourceFile.getAbsolutePath()+" to "
		 * +destFile.getAbsolutePath(),TraceLevel.TRACE);
		 * Files.move(sourceFile.toPath(), destFile.toPath(),
		 * StandardCopyOption.REPLACE_EXISTING, StandardCopyOption.ATOMIC_MOVE);
		 * return true; }catch (Exception e) { try{ trace.trace(
		 * "Non-atomic move from "+sourceFile.getAbsolutePath()+" to "
		 * +destFile.getAbsolutePath(),TraceLevel.TRACE);
		 * Files.move(sourceFile.toPath(), destFile.toPath(),
		 * StandardCopyOption.REPLACE_EXISTING); return true; }catch (Exception
		 * e2) { Util.printStackTrace(trace, e2); return false; } }
		 */
		/*
		 * if (sourceFile.renameTo(destFile)) { trace.trace(
		 * "Moved via java native.",TraceLevel.TRACE); return true; } else {
		 * //assume linux. Call mv external trace.trace("Trying via unix: mv " +
		 * sourceFile.getAbsolutePath()+" "
		 * +destFile.getAbsolutePath(),TraceLevel.TRACE); Process p =
		 * Runtime.getRuntime().exec(new String[]{"/bin/sh","-c",
		 * "mv",sourceFile.getAbsolutePath(),destFile.getAbsolutePath()});
		 * trace.trace("Output from mv"); String line; BufferedReader input =
		 * new BufferedReader (new InputStreamReader(p.getInputStream())); while
		 * ((line = input.readLine()) != null) { System.out.println(line); }
		 * input.close(); trace.trace("Waiting for mv",TraceLevel.TRACE);
		 * p.waitFor();
		 *
		 * trace.trace("Return code: "+p.exitValue(),TraceLevel.TRACE); // try
		 * to copy file, delete original //copyFile(sourceFile, destFile, true,
		 * true); //sourceFile.delete(); } return true; } catch (IOException e)
		 * { trace.trace("IO Exception during moveFile "+sourceFile+" - "
		 * +destFile+" : "+ e.getMessage()); return false; } catch
		 * (InterruptedException e) { trace.trace(
		 * "InterruptedException during moveFile "+sourceFile+" - "+destFile+
		 * " : "+ e.getMessage()); return false; }
		 */
	}

	/**
	 * Append the exception cause to the given status element
	 *
	 * @param statusElem
	 * @param paramThrowable
	 * @param printStackTrace
	 */
	static void appendCause(XDSStatusElement statusElem, Throwable paramThrowable, boolean printStackTrace) {
		if (paramThrowable != null) {
			Throwable cause = paramThrowable.getCause();
			if (cause != null) {
				DocumentImpl paramDocumentImpl = statusElem.documentImpl();
				Element paramElement = statusElem.domElement();

				ExceptionElement exceptionElem = new ExceptionElement(paramDocumentImpl, paramElement);
				exceptionElem.attributeValueSet("class-name", cause.getClass().getName());
				exceptionElem.messageAppend(cause);
				if (printStackTrace) {
					exceptionElem.stackTraceAppend(cause);
				}
			}
		}
	}

	/**
	 * Append the statusAttributes to the statusDoc and return the created
	 * statusElement
	 *
	 * @param statusDoc
	 * @param statusAttr
	 * @param description
	 * @return
	 */
	public static XDSStatusElement appendStatus(StatusDocument statusDoc, StatusAttributes statusAttr,
			String description) {
		XDSStatusElement statusElem = statusDoc.appendStatusElement();
		if (statusAttr != null) {
			statusElem.setLevel(statusAttr.getLevel());
			statusElem.setType(statusAttr.getType());
			statusElem.setEventID(statusAttr.getEventID());
		}
		statusElem.descriptionAppend(description);
		return statusElem;
	}

	/**
	 * Append the given statusattributes to the status document.
	 *
	 * @param statusDoc
	 * @param statusAttr
	 * @param description
	 * @param paramException
	 * @param printStackTrace
	 * @param paramXmlDocument
	 * @return
	 */
	public static XDSStatusElement appendStatus(StatusDocument statusDoc, StatusAttributes statusAttr,
			String description, Throwable paramException, boolean printStackTrace, XmlDocument paramXmlDocument) {
		if (statusDoc != null) {
			XDSStatusElement statusElem = null;
			// Special processing for XDSParameterException: loop
			if ((paramException instanceof XDSParameterException)) {
				for (XDSParameterException localXDSParameterException = (XDSParameterException) paramException; localXDSParameterException != null; localXDSParameterException = localXDSParameterException
						.getNext()) {
					statusElem = appendStatus(statusDoc, statusAttr, localXDSParameterException.getMessage());
				}
				if ((statusElem != null) && (paramXmlDocument != null)) {
					statusElem.documentAppend(paramXmlDocument);
				}
			} else {
				// Make sure we have a decent description
				if (description == null) {
					description = paramException == null ? null : paramException.toString();
				}
				// Append the status element
				statusElem = appendStatus(statusDoc, statusAttr, description);
				// append the exception and the cause
				statusElem.exceptionAppend(paramException, printStackTrace);
				appendCause(statusElem, paramException, printStackTrace);
				statusElem.documentAppend(paramXmlDocument);
			}
			return statusElem;
		}
		return null;
	}

	public static XDSQueryDocument createQueryDoc(String className, String destDN, String association, QueryScope scope,
			Collection<String> readAttr, Map<String, String> matchAttributes) {
		XDSQueryDocument query = new XDSQueryDocument();
		XDSQueryElement queryElem = query.appendQueryElement();
		if (className != null) {
			queryElem.setClassName(className);
		}
		if (destDN != null) {
			queryElem.setDestDN(destDN);
		}
		if (scope != null) {
			queryElem.setScope(scope);
		}
		if (association != null) {
			queryElem.appendAssociationElement(association);
		}
		if (readAttr != null) {
			for (String string : readAttr) {
				XDSReadAttrElement readAttrs = queryElem.appendReadAttrElement();
				readAttrs.setAttrName(string);
			}
		} else {
			// adding an empty readattr element will make it read nothing
			queryElem.appendReadAttrElement();
		}

		if (matchAttributes != null) {
			Set<String> fields = matchAttributes.keySet();
			for (String string : fields) {
				String aFieldName = string;
				String value = matchAttributes.get(aFieldName);
				XDSSearchAttrElement search = queryElem.appendSearchAttrElement();
				search.setAttrName(aFieldName);
				search.appendValueElement(value);
			}
		}
		return query;
	}

	/**
	 * A non-interface method used to format trace values.
	 */
	public static String toLiteral(String string) {
		return (string == null) ? null : "'" + string + "'";
	}// toLiteral(String):String

}
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Date;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import com.novell.nds.dirxml.driver.DriverFilter;
import com.novell.nds.dirxml.driver.Trace;
import com.novell.nds.dirxml.driver.XmlCommandProcessor;
import com.novell.nds.dirxml.driver.XmlDocument;
import com.novell.nds.dirxml.driver.XmlQueryProcessor;
import com.novell.nds.dirxml.driver.xds.Constraint;
import com.novell.nds.dirxml.driver.xds.DataType;
import com.novell.nds.dirxml.driver.xds.EnumConstraint;
import com.novell.nds.dirxml.driver.xds.Parameter;
import com.novell.nds.dirxml.driver.xds.RangeConstraint;
import com.novell.nds.dirxml.driver.xds.RequiredConstraint;
import com.novell.nds.dirxml.driver.xds.StateDocument;
import com.novell.nds.dirxml.driver.xds.StatusLevel;
import com.novell.nds.dirxml.driver.xds.StatusType;
import com.novell.nds.dirxml.driver.xds.XDSAddAttrElement;
import com.novell.nds.dirxml.driver.xds.XDSAddElement;
import com.novell.nds.dirxml.driver.xds.XDSAddValueElement;
import com.novell.nds.dirxml.driver.xds.XDSAttrDefElement;
import com.novell.nds.dirxml.driver.xds.XDSAttrElement;
import com.novell.nds.dirxml.driver.xds.XDSClassDefElement;
import com.novell.nds.dirxml.driver.xds.XDSCommandDocument;
import com.novell.nds.dirxml.driver.xds.XDSCommandResultDocument;
import com.novell.nds.dirxml.driver.xds.XDSDeleteElement;
import com.novell.nds.dirxml.driver.xds.XDSHeartbeatDocument;
import com.novell.nds.dirxml.driver.xds.XDSInitDocument;
import com.novell.nds.dirxml.driver.xds.XDSInitParamsElement;
import com.novell.nds.dirxml.driver.xds.XDSInstanceElement;
import com.novell.nds.dirxml.driver.xds.XDSModifyAttrElement;
import com.novell.nds.dirxml.driver.xds.XDSModifyElement;
import com.novell.nds.dirxml.driver.xds.XDSParameterException;
import com.novell.nds.dirxml.driver.xds.XDSParseException;
import com.novell.nds.dirxml.driver.xds.XDSPublisherStateElement;
import com.novell.nds.dirxml.driver.xds.XDSQueryDocument;
import com.novell.nds.dirxml.driver.xds.XDSQueryElement;
import com.novell.nds.dirxml.driver.xds.XDSQueryResultDocument;
import com.novell.nds.dirxml.driver.xds.XDSReadAttrElement;
import com.novell.nds.dirxml.driver.xds.XDSResultDocument;
import com.novell.nds.dirxml.driver.xds.XDSStatusElement;
import com.novell.nds.dirxml.driver.xds.XDSValueElement;
import com.novell.nds.dirxml.driver.xds.util.StatusAttributes;
import com.novell.nds.dirxml.driver.xds.util.XDSUtil;
import info.vancauwenberge.filedriver.api.IDriver;
import info.vancauwenberge.filedriver.api.IFileLocatorStrategy;
import info.vancauwenberge.filedriver.api.IFileReadStrategy;
import info.vancauwenberge.filedriver.api.IFileSorterStrategy;
import info.vancauwenberge.filedriver.api.IPubFileCleanStrategy;
import info.vancauwenberge.filedriver.api.IPublisherLoggerStrategy;
import info.vancauwenberge.filedriver.api.IPublisherLoggerStrategy.LogField;
import info.vancauwenberge.filedriver.api.IPublisherStrategy;
import info.vancauwenberge.filedriver.api.IShutdown;
import info.vancauwenberge.filedriver.api.IStrategy;
import info.vancauwenberge.filedriver.exception.ReadException;
import info.vancauwenberge.filedriver.exception.WriteException;
import info.vancauwenberge.filedriver.filelocator.RegExpFileLocator;
import info.vancauwenberge.filedriver.filelogger.PublisherNoLogger;
import info.vancauwenberge.filedriver.filepubclean.NoPubCleaningStrategy;
import info.vancauwenberge.filedriver.filereader.csv.CSVFileReader;
import info.vancauwenberge.filedriver.filesorter.NoSortSorter;
import info.vancauwenberge.filedriver.query.QueryMatcher;
import info.vancauwenberge.filedriver.shim.driver.GenericFileDriverShim;
import info.vancauwenberge.filedriver.util.Errors;
import info.vancauwenberge.filedriver.util.MetaDataManager;
import info.vancauwenberge.filedriver.util.TraceLevel;
import info.vancauwenberge.filedriver.util.Util;
		final XDSResultDocument result = driver.newResultDoc();
		StatusAttributes attrs;

		try {
			allParams = new HashMap<String, Parameter>();
			// parse initialization document
			final XDSInitDocument init = new XDSInitDocument(initXML);
			final XDSStatusElement status;
			Parameter param;

			// get any publisher options from init doc
			System.out.println(commonParams);

			final Map<String, Parameter> pubParams = getDefaultParameterDefs();// getShimParameters();
			pubParams.putAll(commonParams);
			init.parameters(pubParams);

			// Add the general driver parameters to the publisher parameters
			pubParams.putAll(driver.getDriverParams());

			// get the polling interval that may have been passed in
			param = pubParams.get(Parameters.POLLING_INTERVAL.getParamName());
			// our pollingInterval value is in seconds, Object.wait(long) takes
			// milliseconds => convert
			pollingInterval = XDSUtil.toMillis(param.toInteger().intValue());

			// get the polling interval that may have been passed in
			param = pubParams.get(PUB_HEARTBEAT_INTERVAL);
			// our heartbeatInterval value is in minutes, Object.wait(long)
			// takes milliseconds => convert
			heartbeatInterval = XDSUtil.toMillis(param.toInteger().intValue() * 60);

			doHeartbeat = (heartbeatInterval > 0);
			interval = (doHeartbeat) ? Math.min(pollingInterval, heartbeatInterval) : pollingInterval;

			// Get the publisher work dir
			workDir = pubParams.get(Parameters.WORK_DIR.getParamName()).toString();
			if (!workDir.endsWith(File.separatorChar + "")) {
				workDir = workDir + File.separatorChar;
			}

			// The command to generate (add or modify)
			commandToGenerate = GenerateCommnd
					.valueOf(pubParams.get(Parameters.COMMAND.getParamName()).toString().toUpperCase());

			if (commandToGenerate == GenerateCommnd.DYNAMIC_INPUT) {
				this.commandIdentifierField = pubParams.get(Parameters.COMMAND_FIELD.getParamName()).toString();
				if ((commandIdentifierField == null) || "".equals(commandIdentifierField.trim())) {
					throw new IllegalArgumentException("Value for " + Parameters.COMMAND_FIELD.getParamName()
							+ " is required when DYNAMIC_INPUT is selected.");
				}
				if (!Arrays.asList(driver.getSchema()).contains(commandIdentifierField)) {
					trace.trace("Warning: Value for " + Parameters.COMMAND_FIELD.getParamName()
							+ " is not part of the schema.This is probably an invalid configuration.");
				}
				final String commandDeleteRegexpStr = pubParams.get(Parameters.COMMAND_DELETE_EGEXP.getParamName())
						.toString();
				commandDeleteRegexp = Pattern.compile(commandDeleteRegexpStr);
			}
			// logField schema map
			String value = pubParams.get(Parameters.LOGFIELD_STATUS.getParamName()).toString();
			if ((value != null) && !value.trim().equals("")) {
				logFieldschemaMap.put(LogField.LOGSTATUS, value);
			}
			value = pubParams.get(Parameters.LOGFIELD_STATUSMESSAGE.getParamName()).toString();
			if ((value != null) && !value.trim().equals("")) {
				logFieldschemaMap.put(LogField.LOGMESSAGE, value);
			}
			value = pubParams.get(Parameters.LOGFIELD_RECORDNUMBER.getParamName()).toString();
			if ((value != null) && !value.trim().equals("")) {
				logFieldschemaMap.put(LogField.RECORDNUMBER, value);
			}
			value = pubParams.get(Parameters.LOGFIELD_EVENTID.getParamName()).toString();
			if ((value != null) && !value.trim().equals("")) {
				logFieldschemaMap.put(LogField.LOGEVENTID, value);
			}
			trace.trace("Log schema:" + logFieldschemaMap);

			// The requested metaData
			param = pubParams.get(Parameters.META_DATA.getParamName());
			trace.trace("MetaData read:" + param);
			metaData = new MetaDataManager(param.toString());
			trace.trace("MetaData result:" + metaData);
			// The fields that should be marked as sensitive
			final String sensitive = pubParams.get(Parameters.SENSITIVE_ATTRIBUTES.getParamName()).toString();
			if ((sensitive != null) && !"".equals(sensitive.trim())) {
				this.sensitiveAttributes = Arrays.asList(sensitive.split(","));
			} else {
				this.sensitiveAttributes = new ArrayList<String>(0);
			}
			initStrategyMap(init, pubParams);

			// Publisher STATE
			initState(init);

			// construct a driver filter for the publication shim to use for
			// filtering application events in start(); in an actual driver,
			// the publisher would use the filter to filter events from the
			// application to avoid publishing unnecessary events to the DirXML
			// engine
			//
			// NOTE: the skeleton publisher doesn't actually make use of the
			// filter, but this code is here to illustrate how to get the
			// publisher filter from the init document
			filter = init.driverFilter();

			// perform any other initialization that might be required

			// append a successful <status> element to the result doc
			attrs = StatusAttributes.factory(StatusLevel.SUCCESS, StatusType.DRIVER_STATUS, null); // event-id
			status = XDSUtil.appendStatus(result, // doc to append to
					attrs, null); // description

			// append the parameter values the publisher is actually using
			allParams.putAll(pubParams);
			status.parametersAppend(allParams);
		} // try
		catch (final ClassCastException e) // don't want to catch Error class
											// with Throwable
		{
			Util.printStackTrace(trace, e);