/**
 * Copyright (C) 2012 - 2016 Alessandro Vurro.
 *
 * Licensed 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 com.googlecode.jmapper.util;

import static com.googlecode.jmapper.config.ResourceLoader.loadResource;
import static com.googlecode.jmapper.util.GeneralUtility.containsAll;
import static com.googlecode.jmapper.util.GeneralUtility.fileSeparator;
import static com.googlecode.jmapper.util.GeneralUtility.isEmpty;
import static com.googlecode.jmapper.util.GeneralUtility.isNull;
import static com.googlecode.jmapper.util.GeneralUtility.list;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

import com.googlecode.jmapper.annotations.JGlobalMap;
import com.googlecode.jmapper.annotations.JMap;
import com.googlecode.jmapper.annotations.JMapAccessor;
import com.googlecode.jmapper.annotations.JMapAccessors;
import com.googlecode.jmapper.config.Error;
import com.googlecode.jmapper.exceptions.LoadingFileException;
import com.googlecode.jmapper.xml.Attribute;
import com.googlecode.jmapper.xml.Global;
import com.googlecode.jmapper.xml.SimplyAttribute;
import com.googlecode.jmapper.xml.beans.XmlJmapper;
import com.thoughtworks.xstream.XStream;
/**
 * FilesManager provides all the operations that allow the manipulation of files.
 *
 * @author Alessandro Vurro
 *
 */
public class FilesManager {
	
	/** application root */
	private static final String applicationRoot = "."+fileSeparator;
	
	/**
	 * This method adds the configurations present in the xml file in the Class.
	 * @param path path of the file that represents the class
	 * @param global global mapping
	 * @param attributes attributes of this Class
	 * @param aClass class to rewrite
	 * @throws NoSuchFieldException thrown if the field doesn't exists
	 * @throws IOException in case of file manipulation problems
	 */	
	public static void addConfigurationToClass(String path,Global global,List<Attribute> attributes,Class<?> aClass) throws NoSuchFieldException, IOException {
		writeFile(new File(path),linesToWrite(path, global, attributes, aClass));
	}
	
	/**
	 * This method returns the lines of the file enriched with annotations.
	 * @param path path of the file that represents the class
	 * @param global global mapping
	 * @param attributes attributes of this Class
	 * @param aClass class to rewrite
	 * @return list of liness to write
	 * @throws NoSuchFieldException 
	 * @throws IOException  
	 */	
	private static List<String> linesToWrite(String path,Global global,List<Attribute> attributes,Class<?> aClass) throws NoSuchFieldException, IOException {
		
		// strings used to identify the class
		String[] classIdentifier = new String[]{"class","{",aClass.getSimpleName()};
		
		// true if lines belong to aClass
		boolean classFound = false;
									
		// verifies that all attributes exist in the class
		verifyAttributes(aClass, attributes);
		
		// retrieves the name and type of each configured field
		HashMap<String, String> attributeTypes = getTypes(aClass, attributes);
		
		// lines to write
		List<String> linesToWrite = new ArrayList<String>();
		
		// lines of the file
		List<String> lines = readFile(new File(path));
		
		// adds of the JMap import
		
		if(!isEmpty(attributes)){
			lines = addImport(lines,aClass, JMap.class);
			// adds of the target classes import
			lines = addTargetClassesImport(lines, attributes,aClass);
		}
		
		if(!isNull(global))
			lines = addImport(lines,aClass, JGlobalMap.class);
		
		if(containtsAccessors(global,attributes)){
			lines = addImport(lines,aClass, JMapAccessor.class);
			lines = addImport(lines,aClass, JMapAccessors.class);
		}
		
		for (String line : lines) {
			
			// If the class declaration has been found
			if(containsAll(line, classIdentifier)){
				// adds the annotation to class
				if(global != null)
					linesToWrite.add(toAnnotation(global));
				classFound = true;
			}
			
			
			if(classFound && !isEmpty(attributes)){
				Attribute remove = null;
				for (Attribute attribute : attributes) {
					String name = attribute.getName();
					String type = attributeTypes.get(name);
					if(containsAll(line,name,type) && !line.contains("{")){
						// adds the annotation to variable
						linesToWrite.add(toAnnotation(attribute));
						remove = attribute;
					}
				}
				if(remove != null)
					attributes.remove(remove);
				
			}
			linesToWrite.add(line);
		}
		
		return linesToWrite;
	}
	
	/**
	 * Returns true if global or attributes containts almost one declaration of custom methods accessor
	 * @param global
	 * @param attributes
	 * @return
	 */
	private static boolean containtsAccessors(Global global,
			List<Attribute> attributes) {
		
		if(!isNull(global)){
			if(!isEmpty(global.getGet()) || !isEmpty(global.getSet()))
				return true;
			
			if(!isEmpty(global.getAttributes()))
				for (SimplyAttribute attribute : global.getAttributes()) 
					if(!isEmpty(attribute.getGet()) || !isEmpty(attribute.getSet()))
						return true;
		}
		
		if(!isEmpty(attributes))
			for (Attribute attribute : attributes) {
				
				if(!isEmpty(attribute.getGet()) || !isEmpty(attribute.getSet()))
					return true;
				
				if(!isEmpty(attribute.getAttributes()))
					for (SimplyAttribute targetAttribute : attribute.getAttributes()) 
						if(!isEmpty(targetAttribute.getGet()) || !isEmpty(targetAttribute.getSet()))
							return true;
			}
		
		return false;
	}

	/**
	 * This method transforms an Global in an annotation in String format.
	 * @param global Global mapping to trasform
	 * @return an annotation in String format
	 */
	private static String toAnnotation(Global global){
		
		// accessors definition
		Attribute attribute = new Attribute(global.getValue(), global.getAttributes());
		attribute.setGet(global.getGet());
		attribute.setSet(global.getSet());
		String accessor = toJMapAccessor(attribute);
		StringBuilder str = new StringBuilder();
		if(!isEmpty(accessor))
			str.append(accessor);
		
		str.append("@JGlobalMap(");
		
		boolean before = false;
		if(global.getValue()!=null){
			str.append("value=\""+global.getValue()+"\"");
			before = true;
		}
		
		SimplyAttribute[] attributes = global.getAttributes();
		if(attributes!=null){
			if(before)str.append(", ");
			else before = true;
			str.append("attributes={");
			for (int i = 0; i < attributes.length; i++) {
				str.append("\""+attributes[i].getName()+"\"");
				if(i<attributes.length-1)str.append(", ");
			}
			str.append("}");
		}
					
		Class<?>[] classes = global.getClasses();
		if(classes!=null){
			if(before)str.append(", ");
			else before = true;
			str.append("classes={");
			for (int i = 0; i < classes.length; i++) {
				str.append(classes[i].getSimpleName()+".class");
				if(i<classes.length-1)str.append(", ");
			}
			str.append("}");
		}
		
		String[] excluded = global.getExcluded();
		if(excluded!=null){
			if(before)str.append(", ");
			str.append("excluded={");
			for (int i = 0; i < excluded.length; i++) {
				str.append("\""+excluded[i]+"\"");
				if(i<excluded.length-1)str.append(", ");
			}
			str.append("}");
		}
			
		str.append(")");
		// If the brackets are empty, returns @JGlobalMap
		if("@JGlobalMap()".equals(str.toString()))return "@JGlobalMap";
		return str.toString();
	}
	
	/**
	 * Build @JMap annotation from Attribute.
	 * @param attribute
	 * @return
	 */
	private static String toJMap(Attribute attribute){
		boolean before = false;
		StringBuilder str = new StringBuilder("@JMap(");
		if(attribute.getValue()!=null){
			str.append("value=\""+attribute.getValue().getName()+"\"");
			before = true;
		}
		
		SimplyAttribute[] attributes = attribute.getAttributes();
		if(attributes!=null){
			if(before)str.append(", ");
			else before = true;
			str.append("attributes={");
			for (int i = 0; i < attributes.length; i++) {
				str.append("\""+attributes[i].getName()+"\"");
				if(i<attributes.length-1)str.append(", ");
			}
			str.append("}");
		}
					
		Class<?>[] classes = attribute.getClasses();
		if(classes!=null){
			if(before)str.append(", ");
			str.append("classes={");
			for (int i = 0; i < classes.length; i++) {
				str.append(classes[i].getSimpleName()+".class");
				if(i<classes.length-1)str.append(", ");
			}
			str.append("}");
		}
		str.append(")");
		// If the brackets are empty, returns @JMap
		if("@JMap()".equals(str.toString()))return "@JMap";
		return str.toString();
	}
	
	private static String toJMapAccessor(String get, String set, String name){
		if(isEmpty(get) && isEmpty(set))  return "";
		
		StringBuilder str = new StringBuilder("@JMapAccessor(name=\""+name+"\"");

		if(!isEmpty(get))
			str.append(", get=\""+get+"\"");
		
		if(!isEmpty(set))
			str.append(", set=\""+set+"\"");
		
		str.append(")");
		return str.toString();
	}
	
	/**
	 * From SimplyAttribute to relative Annotation
	 * @param attribute
	 * @return
	 */
	private static String toJMapAccessor(SimplyAttribute attribute){
		String get = attribute.getGet();
		String set = attribute.getSet();
		String name = attribute.getName();
		return toJMapAccessor(get, set, name);
	}
	
	/**
	 * From an Attribute to relative Annotation
	 * @param attribute
	 * @return
	 */
	private static String toJMapAccessor(Attribute attribute){
		String get = attribute.getGet();
		String set = attribute.getSet();
		String name = attribute.getName();
		
		StringBuilder result = new StringBuilder("@JMapAccessors({");
		if(!isNull(attribute.getAttributes()))
			for (SimplyAttribute targetAtr : attribute.getAttributes()){
				String accessor = toJMapAccessor(targetAtr);
				if(!isEmpty(accessor))
					result.append("\n   "+accessor+",");
			}
		
		
		String accessor = toJMapAccessor(get, set, name);
		if(!isEmpty(accessor))
			result.append("\n   "+accessor);
		else
			if(result.toString().equals("@JMapAccessors({")) 
				return "";
			else
				// deleted last comma
				result = new StringBuilder(result.substring(0, result.length()-1));
			
		
		return result.append("\n})\n").toString();
	}
	/**
	 * This method transforms an Attribute in an annotation in String format.
	 * @param attribute Attribute to trasform
	 * @return an annotation in String format
	 */
	private static String toAnnotation(Attribute attribute){
		
		String accessor = toJMapAccessor(attribute);
		String jMap = toJMap(attribute);
		
		StringBuilder result = new StringBuilder();
		
		if(!isEmpty(accessor))
			result.append(accessor);
		
		if(!isEmpty(jMap))
			result.append(jMap);
		
		return result.toString();
		
	}
	
	/**
	 * Returns a Map with name of the attribute as key and type of the attribute as value.
	 * @param clazz Class to analyze
	 * @param attributes attributes to analyze
	 * @return a Map with name of the attribute as key and type of the attribute as value
	 * @throws SecurityException
	 * @throws NoSuchFieldException
	 */
	private static HashMap<String, String> getTypes(Class<?> clazz, List<Attribute> attributes) throws SecurityException, NoSuchFieldException{
		HashMap<String, String> result = new HashMap<String, String>(attributes.size());
		for (Attribute attribute : attributes) {
			String attributeName = attribute.getName();
			String attributeType = getType(clazz.getDeclaredField(attributeName));
			result.put(attributeName, attributeType);
		}
		return result;
	}
	
	/**
	 * Returns a String that rappresent the type of the Field given as input.
	 * @param field Field to analyze
	 * @return a type of the field in String format
	 */
	private static String getType(Field field){
		String[] dep = field.getType().toString().split(" ");
		dep = dep[dep.length-1].split("\\.");
		return dep[dep.length-1];
	}
	
	/**
	 * This method verifies that the attributes exist in the Class.
	 * @param clazz Class to analyze
	 * @param attributes attributes to analyzez
	 * @throws NoSuchFieldException
	 */
	private static void verifyAttributes(Class<?> clazz, List<Attribute> attributes) throws NoSuchFieldException {
		for (Attribute attribute : attributes) {
			// ottengo il campo dal nome, se non esiste lancio una exception
			try { clazz.getDeclaredField(attribute.getName());}
			catch (SecurityException e) {throw e;} 
			catch (NoSuchFieldException e) {
				Class<?> superclass = clazz.getSuperclass();
				boolean founded = false;
				while(superclass != Object.class && !founded){
					try {	superclass.getDeclaredField(attribute.getName());
							founded = true;
					} catch (NoSuchFieldException e1) {}
					superclass = superclass.getSuperclass();
				}
				if(!founded)throw e;
			}
		}
	}
	
	/**
	 * This method rewrite the file without annotations.
	 * @param file file to rewrite
	 * @param aClass Class that represent the file
	 * @param cleanAll true if all annotation should be delete, false otherwise
	 * @throws IOException in case of read or write errors
	 */
	public static void cleanClass(File file,Class<?> aClass,boolean cleanAll) throws IOException{
		writeFile(file,linesToWrite(file, aClass, cleanAll));
	}

	/**
	 * This method clean the file from annotations and returns a List with the resultant lines.
	 * @param file File to read and clean
	 * @param aClass Class that rappresents this file
	 * @param cleanAll true if all annotation should be deleted, false otherwise
	 * @return a List with lines to write
	 * @throws IOException
	 */
	private static List<String> linesToWrite(File file,Class<?> aClass,boolean cleanAll) throws IOException{
		
		// strings used to identify the class
		String[] classIdentifier = new String[]{"class","{",aClass.getSimpleName()};
		// lines to write
		List<String> linesToWrite = new ArrayList<String>();
		// previous line
		String previousLine = ":-P";
		// true if lines belong to aClass
		boolean classFound = false;
		// true if a class definition was found
		boolean classDefinitionFound = false;
		// true if annotation is written on more lines
		boolean moreLines = false;
		// number of annotated fields
		int annotatedFields = annotatedFieldsNumber(aClass);
		
		for (String line : readFile(file)) {

			// If the class declaration has been found
			if(containsAll(line, classIdentifier))
				classFound = true;
			
			if(containsAll(line, "class","{") && !containsAll(line,"="))
				classDefinitionFound = true;
			
			// if line contains JGlobalMap configuration or if this annotation is written on more lines
			if(!globalToClean(line) && globalToClean(previousLine)){
				if(moreLines && ((cleanAll && classDefinitionFound) || classFound)){
						HashMap<String,Object> cleanLine = cleanLine(previousLine,moreLines,JGlobalMap.class);
						boolean newLine = (Boolean) cleanLine.get("newLine");
						String result = (String) cleanLine.get("result");
						
						if(result != null)
							linesToWrite.add(result);
						
						previousLine = line;
						moreLines = newLine;
						if(!newLine){
							linesToWrite.add(line);
						}
						continue;
				}
				if(!classFound || (cleanAll && !classDefinitionFound)){
					linesToWrite.add(previousLine);
					linesToWrite.add(line);
					previousLine = line;
					classDefinitionFound = false;
					continue;
				}
				linesToWrite.add(line);
				previousLine = line;
				continue;
			}
			
			if(globalToClean(line)){
				previousLine = line;
				continue;
			}
			
			// if line contains JMap configuration or if this annotation is written on more lines
			if((attributeToClean(line) || moreLines) && (cleanAll || (classFound && annotatedFields > 0))){
					
					HashMap<String,Object> cleanLine = cleanLine(line,moreLines,JMapAccessors.class,JMapAccessor.class,JMap.class);
					boolean newLine = (Boolean) cleanLine.get("newLine");
					String result = (String) cleanLine.get("result");
					
					if(result != null)
						linesToWrite.add(result);
					
					moreLines = newLine;
					// countdown is done when we need to clean specific fields
					if(!cleanAll && !moreLines && !newLine)annotatedFields--;
					continue;
			}
			
			if(line.trim().equals("})"))
				continue;

			linesToWrite.add(line);
		}
		
		// delete of JMap import
		linesToWrite = deleteImport(linesToWrite);
		
		return linesToWrite;
	}

	/**
	 * This method clean line from annotation.
	 * @param line line to clean
	 * @param moreLines true if annotation is written on more lines
	 * @annotation annotation to remove
	 * @return an HashMap with two variables: newLine and result
	 */
	private static HashMap<String, Object> cleanLine(String line,boolean moreLines, Class<?>... annotation){
		HashMap<String, Object> map = new HashMap<String, Object>();
		map.put("newLine", moreLines);
		map.put("result", null);
		
		// if the annotation is written on more lines
		// check the closure of the annotation
		if(moreLines){
			String result = verifyLine(line);
			if(!"newLine".equals(result)){
				map.put("newLine", false);
				if(result.trim().length()>0 && !result.trim().equals(","))
					map.put("result", result);
			}
			return map;
		}
		
		// cleans the line from the annotation
		String result = subtractAnnotation(line, annotation);
		// if the line ends with "newLine" the annotation is written on more lines
		if(result.endsWith("newLine")){
			map.put("newLine", true);
			result = result.substring(0,result.length() - "newLine".length());
		}
		
		if(result.trim().length()>0)
			map.put("result", result);
		
		return map;
	}
	
	/**
	 * @param line line to analyze
	 * @return true if the line contains a JMap configuration, false otherwise
	 */
	private static boolean attributeToClean(String line){
		String jmap = "@"+JMap.class.getSimpleName();
		return line.contains(jmap);
	}

	/**
	 * @param line line to analyze
	 * @return true if the line contains a JGlobalMap configuration, false otherwise
	 */
	private static boolean globalToClean(String line){
		return line.contains("@"+JGlobalMap.class.getSimpleName());
	}
	
	/**
	 * @param line
	 * @return a String that contains "newLine" if annotation is written on more lines
	 */
	private static String verifyLine(String line){
		Integer end = line.indexOf(')');
		if(end == -1)return "newLine";
		if(line.length() > ++end)return	line.substring(end, line.length());
		return "";
	}

	/**
	 * It cleans line from annotation.
	 * @param line line to analyze
	 * @param annotation annotation to remove
	 * @return the line cleaned
	 */
	private static String subtractAnnotation(String line, Class<?>... annotations){
		String jmap = null;
		int jmapBegin = -1;
		String result = "";
		
		for (Class<?> annotation : annotations) {
			jmap = "@"+annotation.getSimpleName();
			jmapBegin = line.indexOf(jmap);
			if(jmapBegin != -1)break;
		}
		
		// if there is something before the annotation, it retrieves
		if(jmapBegin>0)result = line.substring(0,jmapBegin);
				
		jmapBegin+=jmap.length();
		// if there is nothing after the annotation, returns result
		if(line.length() <= jmapBegin) return result;
		
		int apertura = line.indexOf('(',jmapBegin);
		// if there is no opening parenthesis of jmap, it adds the remaining line to result
		if(apertura == -1)
			return result += line.substring(jmapBegin,line.length());
		
		// it verifies that there are only spaces between the brackets and the annotation
		else if(apertura > jmapBegin + 1)
			for (char c : line.substring(jmapBegin, apertura).toCharArray()) 
				if(c != ' ')return result += line.substring(jmapBegin,line.length()); 
		
		// it looks for the closure of the annotation
		Integer jmapEnd = line.indexOf(')', jmapBegin);
		// if the closure does not exist, it means that the annotation is written on several lines
		if(jmapEnd == -1) return result+"newLine";
		
		// if the closure exists, add to the result string remaining
		if(line.length() > ++jmapEnd){
			String afterAnnotation = line.substring(jmapEnd,line.length());
			if(!afterAnnotation.trim().equals(","))
			   return result += afterAnnotation;
		}
		return result;
	}
	
	/**
	 * Returns tha number of annotated fields belong to the Class given in input.
	 * @param aClass Class to analyze
	 * @return  the number of annotated fields
	 */
	private static int	annotatedFieldsNumber(Class<?> aClass){
		int count = 0;
		for (Field it : aClass.getDeclaredFields()) 
			if(it.getAnnotation(JMap.class)!=null)count++;
		return count;
	}

	/**
	 * This method adds to lines the import of target classes.
	 * @param lines lines to enrich
	 * @param attributes attributes to analyze
	 * @param aClass Class in question
	 * @return lines enriched
	 */
	private static List<String> addTargetClassesImport(List<String> lines,List<Attribute> attributes,Class<?> aClass){
		
		List<Class<?>> classes = new ArrayList<Class<?>>();
		
		// get all the classes to import
		for (Attribute attribute : attributes) 
			if(attribute.getClasses() != null && attribute.getClasses().length > 0)
				for (Class<?> clazz : attribute.getClasses())
					if(!classes.contains(clazz) && !aClass.getPackage().getName().equals(clazz.getPackage().getName()))
						classes.add(clazz);
		
		// if there aren't classes, returns the lines
		if(classes.isEmpty()) return lines;
						
		// verifies that the classes aren't already imported
		List<Class<?>> alreadyImported = new ArrayList<Class<?>>();
		for (Class<?> clazz : classes) 
				if(existImport(lines, clazz))
					alreadyImported.add(clazz);
		
		// remove from classes those already imported
		classes.removeAll(alreadyImported);
		
	    // writes imports
		List<String> result = new ArrayList<String>();
		for (String line : lines) {
			result.add(line);
			if(!packageFound(line,aClass))continue;
			for (Class<?> clazz : classes) 
				result.add("import "+clazz.getName()+";");
		}
		
		return result;
	}
	
	/**
	 * Adds JMap import to the lines.
	 * @param lines lines to analyze
	 * @param aClass Class in question
	 * @param classToImport class to import
	 * @return lines enriched
	 */
	private static List<String> addImport(List<String> lines,Class<?> aClass, Class<?> classToImport){

		if(existImport(lines, classToImport)) return lines;
		
		List<String> result = new ArrayList<String>();
		for (String line : lines) {
			result.add(line);
			if(!packageFound(line,aClass))continue;
			result.add("import "+classToImport.getName()+";");
		}
		return result;
	}

	/**
	 * Returns true if import already exist, false otherwise.
	 * @param lines lines to check
	 * @param aClass Class in question
	 * @return true if import already exist, false otherwise.
	 */
	private static boolean existImport(List<String> lines, Class<?> aClass){
		for (String line : lines) 
			if(containsAll(line, "import",aClass.getName(),";")) return true;
		return false;
	}

	/**
	 * Cleans lines from the JMap import.
	 * @param lines lines to analyze
	 * @return the resultant lines
	 */
	private static List<String> deleteImport(List<String> lines){
		return deleteSpecificImports(lines, JMap.class,JGlobalMap.class, JMapAccessors.class, JMapAccessor.class);
	}
	
	private static List<String> deleteSpecificImports(List<String> lines, Class<?>... annotations){
		List<String> result = lines;
		for (Class<?> annotation : annotations) 
			result = deleteSpecificImport(result, annotation);
		
		return result;
	}
	
	private static List<String> deleteSpecificImport(List<String> lines, Class<?> annotation){
		
		String[] annImport = {"import", annotation.getName()+";"};
		
		// verifies the annotation presence
		boolean isAnnotated = false;
		for (String line : lines) 
			if(line.contains("@"+annotation.getSimpleName()))
				isAnnotated = true;
				
		// if the class is not annotated, the annotations import will be removed
		if(!isAnnotated){
			List<String> result = new ArrayList<String>();
			for (String line : lines) 
				if(!containsAll(line, annImport)) result.add(line);
					return result;
		}else
			return lines;
	}
	
	/**
	 * Returns true if the line contains the package declaration, false otherwise.
	 * @param line line to check
	 * @param aClass Class in question
	 * @return true if the line contains the package declaration, false otherwise.
	 */
	private static boolean packageFound(String line,Class<?> aClass){
		return containsAll(line, "package",aClass.getPackage().getName(),";");
	}
	/**
	 * Method used to check the file existence, for test purpose.
	 * @param path Path of the file to check
	 * @return true if exists and it's file
	 */
	public static boolean verifyFileExistence(String path){
		File file = new File(path);
		return file.exists() && file.isFile();
	}
	
	/**
	 * Returns the file that has the name given as input, null otherwise.
	 * @param name file name
	 * @return the File with this name
	 * @throws FileNotFoundException thrown if the file isn't found
	 */
	public static File searchFile(String name) throws FileNotFoundException{
		File file = searchFile(new File(applicationRoot),name);
		if(isNull(file))
			Error.fileNotFound(name);
		
		return file;
	}
	
	private static File searchFile(File file,String name){
		if(file.isDirectory())
			for (File it : file.listFiles())
				if(searchFile(it,name) != null) 
					return searchFile(it,name);
		
		if(file.getName().equals(name)) return file;
		return null;
	}
	
	/**
	 * Returns a list with the paths of all java classes.
	 * @return a List with the paths of all java classes
	 * @throws FileNotFoundException if file not found
	 * @throws LoadingFileException if isn't possible load this file
	 */
	public static List<String> classesPath() throws FileNotFoundException, LoadingFileException {
		List<File> files = getJavaFiles(); 
		List<String> paths = new ArrayList<String>();
		
		for (File file : files) 
			paths.add(file.getPath());
				
		return paths;
	}

	/**
	 * Returns a list with all annotated files.
	 * @return a List with all annotated files 
	 * @throws FileNotFoundException if file not found
	 * @throws LoadingFileException if isn't possible load this file
	 * @throws IOException other cases
	 */
	public static List<File> annotatedFiles() throws FileNotFoundException, LoadingFileException, IOException{
		
		List<File> annotatedFiles = new ArrayList<File>();
		for (File javaFile : getJavaFiles()) 
			if(isFileAnnotated(javaFile))
				annotatedFiles.add(javaFile);
		return annotatedFiles;
	}
	
	/**
	 * Returns a list with all annotated classes
	 * @return a List with all annotated classes 
	 * @throws LoadingFileException if isn't possible load this file
	 * @throws ClassNotFoundException class not found
	 * @throws IOException other cases
	 */
	public static List<Class<?>> annotatedClasses() throws LoadingFileException, IOException, ClassNotFoundException{
		
		List<Class<?>> annotatedClasses = new ArrayList<Class<?>>();
		for (File javaFile : annotatedFiles()){
			String fileName = javaFile.getName().substring(0, javaFile.getName().length() - ".java".length());
			annotatedClasses.add(Class.forName(getPackage(javaFile) + "." + fileName));
		}
		return annotatedClasses;
	}
	
	/**
	 * Returns the package of the class represented by this file.
	 * @param file file to check
	 * @return the package of this class
	 * @throws IOException problems with reading the file
	 */
	private static String getPackage(File file) throws IOException{
		for (String line : readFile(file))
			if(line.contains("package")){
				String packageName = line.split(" ")[1];
				return packageName.substring(0, packageName.length()-1);
			}
		return null;
	}
	
	/**
	 * Returns a list of files that are of *.java type.
	 * @param suffix
	 * @return a list with files that have this suffix
	 * @throws FileNotFoundException if file not found
	 * @throws LoadingFileException if isn't possible load this file
	 */
	private static List<File> getJavaFiles() throws FileNotFoundException, LoadingFileException{
		List<File> files = new ArrayList<File>();
		getFiles(new File(applicationRoot),files,".java");
		return files;
	}
	
	/**
	 * Adds to result all the files that have suffix given in input.
	 * @param file file to check
	 * @param result list to enrich
	 * @param suffix suffix to apply
	 */
	private static void getFiles(File file, List<File> result,String suffix){
		if(file.isDirectory())
			for (File it : file.listFiles())
				getFiles(it,result,suffix);
		else 
			if(file.getName().endsWith(suffix))
				result.add(file);
	}
	
	/**
	 * Returns true if the file, relative to path, containts the @JMap annotation.
	 * @param path file path
	 * @param aClass class to check
	 * @return true if the file, relative to path, contains the @JMap annotation
	 * @throws IOException in case of file manipulation problems
	 */
	public static boolean isFileAnnotated(String path, Class<?> aClass) throws IOException{
		return isFileAnnotated(new File(path), aClass);
	}
	/**
	 * Returns true if the file, containts the @JMap annotation.
	 * @param file file to check
	 * @return true if this file contains the @JMap annotation
	 * @throws IOException in case of file manipulation problems
	 */
	private static boolean isFileAnnotated(File file, Class<?> aClass) throws IOException{
		
		// strings used to identify the class
		String[] classIdentifier = new String[]{"class","{",aClass.getSimpleName()};
		
		// true if lines belong to aClass
		boolean classFound = false;
				
		String previuosLine = "";
		for (String line : readFile(file)){
			// If the class declaration has been found
			if(containsAll(line, classIdentifier))
					classFound = true;

			if(classFound && 
			  (previuosLine.contains("@JGlobalMap") || line.contains("@JMap")))return true;
			previuosLine = line;
		}
		return false;
	}
	
	/**
	 * @param file
	 * @return true if this file contains the @JMap annotation
	 * @throws IOException in case of file manipulation problems
	 */
	private static boolean isFileAnnotated(File file) throws IOException{
		for (String line : readFile(file))
			if(line.contains("@JMap") || line.contains("@JGlobalMap"))
				return true;
		return false;
	}
	
	/**
	 * Returns a list with the file lines.
	 * @param file file to read
	 * @return a List with the file lines
	 * @throws IOException in case of file manipulation problems
	 */
	private static List<String> readFile(File file) throws IOException{
		FileReader fr=new FileReader(file);
		BufferedReader br=new BufferedReader(fr);
		List<String> lines = new  ArrayList<String>();
		String line;
		while((line=br.readLine())!=null)lines.add(line);
		fr.close();
		br.close();
		return lines;
	}
	
	/**
	 * Writes the lines given in input in file.
	 * @param file file to write
	 * @param lines lines to write
	 * @throws IOException in case of file manipulation problems
	 */
	private static void writeFile(File file, List<String> lines) throws IOException{
		if (!file.exists()) file.createNewFile();
	                
		FileWriter     fw = new FileWriter(file);
		BufferedWriter Bw = new BufferedWriter(fw);
		PrintWriter    pw = new PrintWriter(Bw);
	
		for (String line : lines)pw.println(line);
	
		Bw.close();
		fw.close();
		pw.close();
	}
	
	/**
	 * This method writes the xml file starting from an XmlJmapper object, following the xmlPath.
	 * @param jmapper XmlJmapper object that will be used for write the xml mapping file
	 * @param xmlPath xml mapping file path
	 * @throws IOException in case of file manipulation problems
	 */
	public static void write(XmlJmapper jmapper, String xmlPath) throws IOException{
		XStream xstream = new XStream();
		xstream.processAnnotations(XmlJmapper.class);
		writeFile(new File(xmlPath),list(xstream.toXML(jmapper)));
	}
	
	/**
	 * This method loads the xml file relative to xmlPath parameter.
	 * Read method is used for the xml manipulation at development time.
	 * 
	 * @param xmlPath path to xml file
	 * @return XmlJmapper object
	 * @throws FileNotFoundException if file not found
	 */
	public static XmlJmapper readAtDevelopmentTime(String xmlPath) throws FileNotFoundException{
		return toXmlJmapper(xmlPath,new FileInputStream(searchFile(xmlPath)));
	}
	
	/**
	 * This method loads the xml file relative to xmlPath parameter.
	 * Read method is used for the xml manipulation at runtime.
	 * 
	 * @param xmlPath path to xml file
	 * @return XmlJmapper object
	 * @throws MalformedURLException in case of malformed url
	 * @throws IOException other cases
	 */
	public static XmlJmapper readAtRuntime(String xmlPath) throws MalformedURLException, IOException{
		return toXmlJmapper(xmlPath,loadResource(xmlPath));
	}
	
	/**
	 * Converts an inputStream in XmlJmapper using XStream library.
	 * @param path file path
	 * @param is stream to convert
	 * @return an enriched XmlJmapper object
	 * @throws FileNotFoundException if file not found
	 */
	private static XmlJmapper toXmlJmapper(String path, InputStream is) throws FileNotFoundException{
		XStream xstream = new XStream();
		xstream.processAnnotations(XmlJmapper.class);
		if(is != null) return (XmlJmapper) xstream.fromXML(is);
		Error.fileNotFound(path);
		return null;
	}
	
	/**
	 * Starting from filename returns its path.
	 * @param fileName file name
	 * @return the absolute path
	 * @throws FileNotFoundException thrown if file not found
	 */
	public static String fullPathOf(String fileName) throws FileNotFoundException{
		return searchFile(fileName).getAbsolutePath();
	}

	/**
	 * @param resource resource to check
	 * @return true if resource is a path
	 */
	public static boolean isPath(String resource){
		return resource.endsWith(".xml") || resource.endsWith(".properties");
	}
}