Index: tika-core/src/main/java/org/apache/tika/sax/ChmWriteOutContentHandler.java
===================================================================
--- tika-core/src/main/java/org/apache/tika/sax/ChmWriteOutContentHandler.java	(revision 0)
+++ tika-core/src/main/java/org/apache/tika/sax/ChmWriteOutContentHandler.java	(revision 0)
@@ -0,0 +1,206 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.chm;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+
+//----------------------------------------------
+//
+//	The same like WriteOutContentHandler class
+//  with minor change on line 127
+//
+//----------------------------------------------
+
+
+/**
+ * SAX event handler that writes all character content out to
+ * a {@link Writer} character stream.
+ */
+public class ChmWriteOutContentHandler extends DefaultHandler {
+
+    /**
+     * The character stream.
+     */
+    private final Writer writer;
+
+    /**
+     * The maximum number of characters to write to the character stream.
+     * Set to -1 for no limit.
+     */
+    private final int writeLimit;
+
+    /**
+     * Number of characters written so far.
+     */
+    private int writeCount = 0;
+
+    private ChmWriteOutContentHandler(Writer writer, int writeLimit) {
+        this.writer = writer;
+        this.writeLimit = writeLimit;
+    }
+
+    /**
+     * Creates a content handler that writes character events to
+     * the given writer.
+     *
+     * @param writer writer
+     */
+    public ChmWriteOutContentHandler(Writer writer) {
+        this(writer, -1);
+    }
+
+    /**
+     * Creates a content handler that writes character events to
+     * the given output stream using the default encoding.
+     *
+     * @param stream output stream
+     */
+    public ChmWriteOutContentHandler(OutputStream stream) {
+        this(new OutputStreamWriter(stream));
+    }
+
+    /**
+     * Creates a content handler that writes character events
+     * to an internal string buffer. Use the {@link #toString()}
+     * method to access the collected character content.
+     * <p>
+     * The internal string buffer is bounded at the given number of characters.
+     * If this write limit is reached, then a {@link SAXException} is thrown.
+     * The {@link #isWriteLimitReached(Throwable)} method can be used to
+     * detect this case.
+     *
+     * @since Apache Tika 0.7
+     * @param writeLimit maximum number of characters to include in the string,
+     *                   or -1 to disable the write limit
+     */
+    public ChmWriteOutContentHandler(int writeLimit) {
+        this(new StringWriter(), writeLimit);
+    }
+
+    /**
+     * Creates a content handler that writes character events
+     * to an internal string buffer. Use the {@link #toString()}
+     * method to access the collected character content.
+     * <p>
+     * The internal string buffer is bounded at 100k characters. If this
+     * write limit is reached, then a {@link SAXException} is thrown. The
+     * {@link #isWriteLimitReached(Throwable)} method can be used to detect
+     * this case.
+     */
+    public ChmWriteOutContentHandler() {
+        this(100 * 1000);
+    }
+
+    /**
+     * Writes the given characters to the given character stream.
+     * 
+     * Note: I modified this function due to throwing an exception
+     * @changed since May 24, 2010
+     */
+    @Override
+    public void characters(char[] ch, int start, int length)
+            throws SAXException {
+        try {
+            if (writeLimit == -1 || writeCount + length <= writeLimit) {
+                writer.write(ch, start, ch.length); /* here is a changing */
+                writeCount += length;
+            } else {
+                writer.write(ch, start, ch.length - writeCount);
+                writeCount = writeLimit;
+                throw new WriteLimitReachedException();
+            }
+        } catch (IOException e) {
+            throw new SAXException("Error writing out character content", e);
+        }
+    }
+
+
+    /**
+     * Writes the given ignorable characters to the given character stream.
+     */
+    @Override
+    public void ignorableWhitespace(char[] ch, int start, int length)
+            throws SAXException {
+        characters(ch, start, length);
+    }
+
+    /**
+     * Flushes the character stream so that no characters are forgotten
+     * in internal buffers.
+     *
+     * @see <a href="https://issues.apache.org/jira/browse/TIKA-179">TIKA-179</a>
+     * @throws SAXException if the stream can not be flushed
+     */
+    @Override
+    public void endDocument() throws SAXException {
+        try {
+            writer.flush();
+        } catch (IOException e) {
+            throw new SAXException("Error flushing character output", e);
+        }
+    }
+
+    /**
+     * Returns the contents of the internal string buffer where
+     * all the received characters have been collected. Only works
+     * when this object was constructed using the empty default
+     * constructor or by passing a {@link StringWriter} to the
+     * other constructor.
+     */
+    @Override
+    public String toString() {
+        return writer.toString();
+    }
+
+    /**
+     * Checks whether the given exception (or any of it's root causes) was
+     * thrown by this handler as a signal of reaching the write limit.
+     *
+     * @since Apache Tika 0.7
+     * @param t throwable
+     * @return <code>true</code> if the write limit was reached,
+     *         <code>false</code> otherwise
+     */
+    public boolean isWriteLimitReached(Throwable t) {
+        if (t instanceof WriteLimitReachedException) {
+            return this == ((WriteLimitReachedException) t).getSource();
+        } else {
+            return t.getCause() != null && isWriteLimitReached(t.getCause());
+        }
+    }
+
+    /**
+     * The exception used as a signal when the write limit has been reached.
+     */
+    private class WriteLimitReachedException extends SAXException {
+		private static final long serialVersionUID = 1L;
+
+		public ChmWriteOutContentHandler getSource() {
+            return ChmWriteOutContentHandler.this;
+        }
+
+    }
+
+}
Index: tika-parsers/src/main/java/org/apache/tika/parser/chm/CHM2XHTML.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/chm/CHM2XHTML.java	(revision 0)
+++ tika-parsers/src/main/java/org/apache/tika/parser/chm/CHM2XHTML.java	(revision 0)
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tika.parser.chm;
+
+import org.apache.tika.sax.TextContentHandler;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+
+/**
+ * Utility class
+ * 
+ *
+ */
+public class CHM2XHTML {
+
+	public static void process(CHMDocumentInformation chmDoc, ContentHandler handler) {
+		String text = chmDoc.getText();
+		try {
+			if(text.length() > 0){
+				handler.characters(text.toCharArray(), 0, text.length());
+				new CHM2XHTML(chmDoc,handler);
+			}
+			else /* The error handling should be added */
+				System.err.println("Could not extract content");
+			
+		} catch (SAXException e) {
+//			System.err.println(ChmParserUtils.getStackTrace(e.getStackTrace()));
+		}
+	}
+
+	protected String getText(CHMDocumentInformation chmDoc){
+		return chmDoc.getText();
+	}
+	
+	protected TextContentHandler handler;
+	
+	public CHM2XHTML(CHMDocumentInformation chmDoc,ContentHandler handler) {
+		this.handler = new TextContentHandler(handler);
+	}
+}
Index: tika-parsers/src/main/java/org/apache/tika/parser/chm/CHMDocumentInformation.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/chm/CHMDocumentInformation.java	(revision 0)
+++ tika-parsers/src/main/java/org/apache/tika/parser/chm/CHMDocumentInformation.java	(revision 0)
@@ -0,0 +1,524 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.tika.parser.chm;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.html.HtmlParser;
+import org.apache.tika.sax.BodyContentHandler;
+import org.yufeng.jchmlib.ChmManager;
+import org.yufeng.jchmlib.FileEntry;
+
+
+/**
+ * Contains information about chm file
+ * Provides chm Metadata and Content
+ * 
+ * Metadata - should be discussed what is a metadata desired 
+ *
+ */
+public class CHMDocumentInformation {
+	/* Class members */
+	/**
+	 * Meta data that can be extracted from hhp file. This file does not
+	 * always exist.
+	 */
+	private final String[] patterns = {"Auto Index","Binary TOC","Binary Index","Compatibility",
+			                     "Compiled File","Contents File","Default Topic","Error log file",
+			                     "Index File","Title","Display compile progress","Full-text search",
+			                     "Default Window","Default Font","Language","Create CHI file"};
+
+	
+	private final String tempChmFileName = "tempFile.chm";
+	private InputStream chmStream = null;
+	private CHMDocumentInformation chmDocument = null;
+	private ChmManager chmManager = null;
+	private String chmPath = null;
+	private InputStream hhpStream = null;
+	
+	/**
+	 * Loads chm file from InputStream
+	 * 
+	 * @param stream - InputStream
+	 * 
+	 * @return CHMDocumentInformation
+	 */
+	public static CHMDocumentInformation load(InputStream stream){
+		return new CHMDocumentInformation(stream);
+	}
+	
+	
+	/**
+	 * Loads chm file from the path to that file
+	 * 
+	 * @param chmPath - String
+	 * @return CHMDocumentInformation
+	 */
+	public static CHMDocumentInformation load(String chmPath){
+		return new CHMDocumentInformation(chmPath);
+	}
+	
+	/**
+	 * Returns a metadata from inputStream that contains a metadata
+	 * HHP file contains chm metadata, order of files to be extracted
+	 * 
+	 * Note: This file does not always exist
+	 * 
+	 * @param metadata - Metadata
+	 */
+	public void getCHMDocInformation(Metadata metadata){
+		setHhpStream(getCHMPC("hhp"));
+		if(getHhpStream() != null){
+			addMetadata(metadata, patterns);//hhpFileName, 
+		}else{
+			metadata.add(Metadata.CONTENT_TYPE, "application/x-chm");
+		}	
+	}
+	
+	
+	/**
+	 * Returns text extracted from html files containing in chm file
+	 * 
+	 * @return String
+	 */
+	public String getText(){
+		return getContent();
+	}
+	
+
+
+	/**
+	 * Adds metadata of chm file
+	 * 
+	 * @param metData - Metadata
+	 * @param is - InputStream
+	 * @param metatda - String[[]
+	 */
+	public void addMetadata(Metadata metadata, String ...paterns) {//InputStream is,
+		metadata.add(Metadata.CONTENT_TYPE, "application/x-chm");
+		if (getHhpStream() != null) {
+			String line;
+			try {
+				BufferedReader reader = new BufferedReader(new InputStreamReader(getHhpStream(), "UTF-8"));//synced
+				int index = 0;
+
+				while ((line = reader.readLine()) != null || index <= paterns.length) {
+					if (line.contains(patterns[index])) {
+						metadata.add(patterns[index], line.substring(line.lastIndexOf("=") + 1));
+						index++;
+					}
+				}
+				reader.close();
+				getHhpStream().close();
+				
+			} catch (Exception e) {
+//				System.err.println(getStackTrace(e.getStackTrace()));
+			}
+		}
+	}
+	
+
+	/**
+	 * Creates CHMDocumentInformation object from InputStream
+	 * 
+	 * @param stream - InputStream
+	 */
+	private CHMDocumentInformation(InputStream stream){
+		setChmStream(stream);
+		setChmManager(new ChmManager(getFile(stream).getName()));
+	}
+	
+	
+	/**
+	 * Creates CHMDocumentInformation from path to the chm file
+	 * 
+	 * @param chmPath - String
+	 */
+	private CHMDocumentInformation(String chmPath){
+		
+		try {
+			setChmStream(new FileInputStream(new File(chmPath)));
+			setChmPath(chmPath);
+			setChmManager(new ChmManager(getChmPath()));
+			
+		} catch (FileNotFoundException e) {
+			e.printStackTrace();
+		}
+	}
+	
+	
+	/**
+	 * Returns the file content
+	 * 
+	 * @return String
+	 */
+	@SuppressWarnings({ "unchecked" })
+	private String getContent(){//File file
+		ArrayList<FileEntry> list = getChmManager().enumerateFiles();
+		List<String> orderedList = null;
+		try {
+			/* If hhp file exists we can extract text from files in logical way - the same order like it exists in the book */
+			orderedList = getOrderedFilesList(getCHMPC(list, "hhp"));
+			/* Cannot delete file while other process uses it */
+		} catch (Exception e) {
+//			System.err.println("getContent(...) [ " + file.getName() + " ] thrown an exception:= " + e.getLocalizedMessage());
+		}
+		WeakReference<StringBuilder> sb = new WeakReference<StringBuilder>(new StringBuilder());
+		try {
+			if(orderedList != null && !orderedList.isEmpty()){
+				for (int index = 0; index < list.size(); index++) {
+					try {
+						sb.get().append(extract(new WeakReference<byte[][]>(getChmManager().retrieveObject(list.get(index))).get()));
+					} catch (Exception e) {
+						// System.err.println(getStackTrace(e.getStackTrace()));
+					}
+				}
+			}else{/* If hhp file does not exist, then extract text randomly - here performance is better then in previous case */
+				sb.get().append(extractNoOrder(getChmManager(), list));
+			}
+		} catch (Exception e) {
+//			System.err.println("Problem with [" + getChmManager(). + "] file" + System.getProperty("line.separator"));
+		}
+		return sb.get().toString();
+	}
+	
+	
+	/**
+	 * Extracts text from byte array 
+	 * 
+	 * @param byteObject
+	 * @return
+	 */
+	private String extract(byte[][] byteObject){
+		WeakReference<StringBuilder> wBuf = new WeakReference<StringBuilder>(new StringBuilder());
+		try {
+			for (int i = 0; i < byteObject.length; i++) {
+				InputStream stream = new ByteArrayInputStream(byteObject[i]);
+				Metadata metadata = new Metadata();
+				HtmlParser htmlParser = new HtmlParser( );
+				BodyContentHandler handler = new BodyContentHandler();
+				htmlParser.parse(stream, handler , metadata, new ParseContext());
+				wBuf.get().append(handler.toString() + System.getProperty("line.separator"));
+				stream.close();
+			}
+		} catch (Exception e) {
+		}
+		return wBuf.get().toString();
+	}
+	
+	/**
+	 * Extracts text from files passed as List
+	 * The order of extracted files won't be logical
+	 * 
+	 * @param cm ChmManager
+	 * @param list ArrayList<FileEntry>
+	 * 
+	 * @return extracted text String
+	 */
+	//----------------------------------------------------------
+	// TODO: Find some way to fix a bug in LzxBlock of jchm jar
+	// bug is fixed but how do I enter changes ?????
+	//----------------------------------------------------------
+	
+	
+	private String extractNoOrder(ChmManager cm, ArrayList<FileEntry> list){
+		WeakReference<StringBuilder> wBuf = new WeakReference<StringBuilder>(new StringBuilder());
+		for(FileEntry fe : list){
+			if(fe.entryName.substring(fe.entryName.lastIndexOf(".")+1).contains("html") || fe.entryName.substring(fe.entryName.lastIndexOf(".")+1).contains("htm")){
+				try {
+					wBuf.get().append(extract(new WeakReference<byte[][]>(cm.retrieveObject(fe)).get()));
+				} catch (Exception e) {
+//					e.printStackTrace();
+				}
+			}
+		}
+		return wBuf.get().toString();
+	}
+	
+	/**
+	 * Returns either hhc or hhp
+	 * 
+	 * @param list - FileEntry list
+	 * @param extension - String
+	 * 
+	 * @return InputStream
+	 */
+	private InputStream getCHMPC(ArrayList<FileEntry> list, String extension){
+		int index = getIndexOf(list, extension);
+		InputStream stream = null;
+		if(index >= 0){
+		    stream = new ByteArrayInputStream(merge(getChmManager().retrieveObject(list.get(index))));
+		}
+		return stream;
+	}
+	
+	
+	/**
+	 * Returns index of FileEntry
+	 * 
+	 * @param list - ArrayList<FileEntry>
+	 * @param extension - String
+	 * @return
+	 */
+	private int getIndexOf(ArrayList<FileEntry> list, String extension){
+		int temp = 0, index = -1;
+		boolean exit = false;
+		if(list != null && !list.isEmpty()){
+			Iterator<FileEntry> it = list.iterator();
+			while (it.hasNext() && !exit) {
+				FileEntry fe = (FileEntry) it.next();
+				if(fe.entryName.substring(fe.entryName.lastIndexOf(".") + 1).equals(extension)){
+					try {
+						byte[][] byteObject = getChmManager().retrieveObject(fe);
+						if(byteObject != null && byteObject.length > 0){
+							index = temp;
+							exit = true;
+						}		
+					} catch (Exception e) {
+						System.err.println("getIndexOf(...) [ " + fe.entryName + " ] wrong ..." );
+					}
+				}
+				++temp;
+			}
+		}
+		return index;
+	}
+	
+	
+	/**
+	 * Returns either hhc or hhp
+	 * 
+	 * @param extension - Stirng
+	 * @return InputStream
+	 */
+	@SuppressWarnings("unchecked")
+	private InputStream getCHMPC(String extension){
+		ArrayList<FileEntry> list = getChmManager().enumerateFiles();
+		InputStream stream = null;
+		boolean exit = false;
+		if(list != null && !list.isEmpty()){
+			Iterator<FileEntry> it = list.iterator();
+			while (it.hasNext() && !exit) {
+				FileEntry fe = (FileEntry) it.next();
+				if(fe.entryName.substring(fe.entryName.lastIndexOf(".") + 1).equals(extension)){
+					try {
+						byte[][] byteObject = getChmManager().retrieveObject(fe);
+						if(byteObject != null && byteObject.length > 0){
+							stream = new ByteArrayInputStream(merge(byteObject));
+							exit = true;
+						}		
+					} catch (Exception e) {
+						System.err.println("getCHMPC(...) [ " + fe.entryName + " ] wrong ..." );
+						try {
+							stream.close();
+						} catch (Exception e2) {
+							// TODO: handle exception
+						}
+					}
+				}
+
+			}
+		}else 
+			System.err.println(CHMDocumentInformation.class.getName() + " File list is empty");
+		return stream;
+	}
+	
+	
+	/**
+	 * Merges byte arrays to bigger one
+	 * 
+	 * @param first - byte[][]
+	 * @return byte[]
+	 */
+	private synchronized byte[] merge(byte[][] first){
+		int total = 0;
+		for(int i=0; i < first.length; i++)
+			total += total + first[i].length;
+		byte[] appendedArray = new byte[total];
+		int currentPosition = 0;
+		
+		for(int i=0; i < first.length; i++){
+			System.arraycopy(first[i], 0, appendedArray, currentPosition, first[i].length);
+			currentPosition += currentPosition + first[i].length;
+		}
+		return appendedArray;
+	}
+	
+	/**
+	 * Returns a list of files to be extracted in logical order
+	 * 
+	 * @param stream
+	 * @return List<String>
+	 */
+	private List<String> getOrderedFilesList(InputStream stream) {
+		List<String> orderedList = new ArrayList<String>();
+		if(stream != null){
+			try {
+				BufferedReader input = new BufferedReader(new InputStreamReader(stream, "UTF-8"));
+				String line = null;
+				while ((line = input.readLine()) != null) {
+					if (line.substring(line.lastIndexOf(".") + 1).contains("html") || line.substring(line.lastIndexOf(".") + 1).contains("htm")) {
+						orderedList.add(line.substring(line.lastIndexOf("=") + 2, line.lastIndexOf("\"")));
+					}
+				}
+				input.close();
+				stream.close();
+			} catch (Exception e) {		}
+		}
+		return orderedList;
+	}
+	
+	/**
+	 * Returns chm file created from stream
+	 * 
+	 * @param inputStream
+	 * @return chm - File
+	 */
+	private synchronized File getFile(InputStream inputStream) {
+		WeakReference<File> tempFile = new WeakReference<File>(new File(tempChmFileName));
+		if(tempFile.get().exists()){
+			tempFile.get().deleteOnExit();
+		}
+		try {
+			OutputStream out = new FileOutputStream(tempFile.get());
+			byte buf[] = new byte[1024];
+			int len;
+			while ((len = inputStream.read(buf)) > 0){
+				out.write(buf, 0, len);
+			}
+			out.close();
+			inputStream.close();
+		} catch (IOException e) {
+			e.printStackTrace();
+		}
+		return tempFile.get();
+	}
+	
+	
+	public static void main(String[] args){
+		String chmName = "bad_file/Addison.Wesley,.The.J2EE.Tutorial.(2004),.2Ed.DDU.chm";
+		try {
+			/* Case 1: init object from InputStream */
+//			CHMDocumentInformation chmDocInfo = CHMDocumentInformation.load(new FileInputStream(new File(chmName)));
+			
+			/* Case 2: init object from path to the chm file */
+			CHMDocumentInformation chmDocInfo = CHMDocumentInformation.load(new File(chmName).getCanonicalPath());
+			Metadata md = new Metadata();
+			chmDocInfo.getCHMDocInformation(md);
+			System.out.println(new File(chmName).getName() + " " + chmDocInfo.getText().length() + "\nMetadata " + md);
+		} catch (Exception e1) {
+			e1.printStackTrace();
+		}
+	}
+
+
+	/**
+	 * Sets a InputStream 
+	 * 
+	 * @param chmStream - InputStream
+	 */
+	public void setChmStream(InputStream chmStream) {
+		this.chmStream = chmStream;
+	}
+
+	
+	/**
+	 * Returns an InputStream
+	 * 
+	 * @return chm stream - InputStream
+	 */
+	public InputStream getChmStream() {
+		return chmStream;
+	}
+
+	/**
+	 * Seats a CHMDocumentInformation
+	 * 
+	 * @param chmDocument - CHMDocumentInformation
+	 */
+	public void setChmDocument(CHMDocumentInformation chmDocument) {
+		this.chmDocument = chmDocument;
+	}
+
+	public CHMDocumentInformation getChmDocument() {
+		return chmDocument;
+	}
+
+	/**
+	 * @param chmPath the chmPath to set
+	 */
+	public void setChmPath(String chmPath) {
+		this.chmPath = chmPath;
+	}
+
+	/**
+	 * @return the chmPath
+	 */
+	public String getChmPath() {
+		return chmPath;
+	}
+
+
+	/**
+	 * @param chmManager the chmManager to set
+	 */
+	public void setChmManager(ChmManager chmManager) {
+		this.chmManager = chmManager;
+	}
+
+
+	/**
+	 * @return the chmManager
+	 */
+	public ChmManager getChmManager() {
+		return chmManager;
+	}
+
+
+	/**
+	 * @param hhpStream the hhpStream to set
+	 */
+	public void setHhpStream(InputStream hhpStream) {
+		this.hhpStream = hhpStream;
+	}
+
+
+	/**
+	 * @return the hhpStream
+	 */
+	public InputStream getHhpStream() {
+		return hhpStream;
+	}
+	
+}
Index: tika-parsers/src/main/java/org/apache/tika/parser/chm/ChmParser.java
===================================================================
--- tika-parsers/src/main/java/org/apache/tika/parser/chm/ChmParser.java	(revision 0)
+++ tika-parsers/src/main/java/org/apache/tika/parser/chm/ChmParser.java	(revision 0)
@@ -0,0 +1,65 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.parser.chm;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.tika.exception.TikaException;
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.mime.MediaType;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.Parser;
+import org.xml.sax.ContentHandler;
+import org.xml.sax.SAXException;
+
+
+public class ChmParser implements Parser {
+	
+	private static final Set<MediaType> SUPPORTED_TYPES =
+        Collections.singleton(MediaType.application("chm"));
+
+	@Override
+	public Set<MediaType> getSupportedTypes(ParseContext context) {
+		return SUPPORTED_TYPES;
+	}
+
+	@Override
+	/**
+	 * @Deprecated This method will be removed in Apache Tika 1.0.
+	 */
+	public void parse(InputStream stream, ContentHandler handler, Metadata metadata) throws IOException, SAXException, TikaException {
+		parse(stream,handler,metadata, new ParseContext());
+	}
+
+	@Override
+	public void parse(InputStream stream, ContentHandler handler, Metadata metadata, ParseContext context) throws IOException, SAXException, TikaException {
+		CHMDocumentInformation chmInfo = CHMDocumentInformation.load(stream);
+		metadata.set(Metadata.CONTENT_TYPE, "chm");
+		extractMetadata(chmInfo, metadata);
+		CHM2XHTML.process(chmInfo,handler);
+	}
+	
+	private void extractMetadata(CHMDocumentInformation chmInfo, Metadata metadata){
+		/* Using File */
+		//		chmInfo.getCHMDocInformation(metadata);
+		/* Using InputStream*/
+		chmInfo.getCHMDocInformation(metadata);
+	}
+}
Index: tika-parsers/src/test/java/org/apache/tika/parser/chm/ChmParserTest.java
===================================================================
--- tika-parsers/src/test/java/org/apache/tika/parser/chm/ChmParserTest.java	(revision 0)
+++ tika-parsers/src/test/java/org/apache/tika/parser/chm/ChmParserTest.java	(revision 0)
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tika.chmparser.tests;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.InputStream;
+import java.util.Date;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+import junit.framework.Assert;
+import junit.framework.TestCase;
+
+import org.apache.tika.metadata.Metadata;
+import org.apache.tika.parser.ParseContext;
+import org.apache.tika.parser.chm.ChmParser;
+import org.apache.tika.parser.chm.ChmWriteOutContentHandler;
+import org.apache.tika.sax.TextContentHandler;
+import org.xml.sax.ContentHandler;
+
+
+public class ChmParserTest extends TestCase {
+	/* Class members */
+	private ChmParser chmParser = null;
+	private final String folderName = "bad_file";
+	private File[] files = null;
+	private File dir = null;
+	
+	
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	protected void setUp() throws Exception {
+		super.setUp();
+		this.setChmParser(new ChmParser());
+		this.setDir(new File(this.getFolderName()));
+		this.setFiles(dir.listFiles());
+	}
+	
+	/**
+	 * Tests chm parser
+	 */
+	public void testChmParser(){
+		for(File file : this.getFiles()){
+			try {
+				InputStream stream = new FileInputStream(new File(this.getDir() + File.separator + file.getName()));
+				Metadata md = new Metadata();
+				ContentHandler handler = new TextContentHandler(new ChmWriteOutContentHandler(-1));
+				chmParser.parse(stream, handler , md, new ParseContext());
+				Assert.assertEquals(handler.toString().isEmpty(), false);
+				Assert.assertEquals("chm", md.get("Content-Type").toString());
+				stream.close();
+			} catch (Exception e) {
+//				e.printStackTrace();
+			}
+		}
+	}
+	
+	/**
+	 * Doesn't work currently ....
+	 * 
+	 */
+	public void testMultiThreading(){
+		int NTHREADS = 1;
+//		System.out.println(new ThreadTest(getFolderName()).getFiles().toString());
+
+		ExecutorService executor = Executors.newFixedThreadPool(NTHREADS);
+		long start = new Date().getTime();
+		for (int i = 0; i < NTHREADS; i++) {
+			Runnable extractor = new ThreadTest(getFolderName());
+			executor.execute(extractor);
+		}
+		// This will make the executor accept no new threads
+		// and finish all existing threads in the queue
+		executor.shutdown();
+		// Wait until all threads are finish
+		try {
+			while (!executor.isTerminated()) {
+				Thread.sleep(500);
+			}
+		} catch (Exception e) {
+			// TODO: handle exception
+		}
+		long end = new Date().getTime();
+		System.out.println("Finished all threads, took " + (end - start) + " in msecs");
+	}
+	
+	
+
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	protected void tearDown() throws Exception {
+		super.tearDown();
+	}
+
+
+	//=======================================\\
+	// 			Getters and Setters			 ||
+	//=======================================//
+	/**
+	 * @param chmParser the chmParser to set
+	 */
+	protected void setChmParser(ChmParser chmParser) {
+		this.chmParser = chmParser;
+	}
+
+
+	/**
+	 * @return the chmParser
+	 */
+	protected ChmParser getChmParser() {
+		return chmParser;
+	}
+
+
+	/**
+	 * @return the folderName
+	 */
+	protected String getFolderName() {
+		return folderName;
+	}
+
+
+	/**
+	 * @param files the files to set
+	 */
+	protected void setFiles(File[] files) {
+		this.files = files;
+	}
+
+
+	/**
+	 * @return the files
+	 */
+	protected File[] getFiles() {
+		return files;
+	}
+	
+	protected File getDir(){
+		return this.dir;
+	}
+	
+	protected void setDir(File folder){
+		this.dir = folder;
+	}
+}
