WADLUtilities.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */



package org.openspcoop2.utils.wadl;

import java.io.File;
import java.net.URI;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.jvnet.ws.wadl.HTTPMethods;
import org.jvnet.ws.wadl.ast.ApplicationNode;
import org.jvnet.ws.wadl.ast.MethodNode;
import org.jvnet.ws.wadl.ast.ResourceNode;
import org.openspcoop2.utils.resources.FileSystemUtilities;
import org.openspcoop2.utils.transport.http.HttpRequestMethod;
import org.openspcoop2.utils.xml.AbstractXMLUtils;
import org.slf4j.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;




/**
 * Utilities per i WADL
 *
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */


public class WADLUtilities {


	//	public static WADLUtilities getInstance(){
	//		return new WADLUtilities(null); // senza xmlUtils
	//	}
	public static WADLUtilities getInstance(AbstractXMLUtils xmlUtils){
		return new WADLUtilities(xmlUtils); 
	}




	private AbstractXMLUtils xmlUtils = null;
	public WADLUtilities(AbstractXMLUtils xmlUtils){
		this.xmlUtils = xmlUtils;
	}






	/* ---------------- METODI STATICI PUBBLICI ------------------------- */

	/** WADLReader */
	public WADLReader getWADLReader(Logger log, boolean verbose, boolean processInclude, boolean processInlineSchema){
		return new WADLReader(log, this.xmlUtils, verbose, processInclude,processInlineSchema);
	}



	// IS WADL
	public boolean isWADL(byte[]wadl) throws org.openspcoop2.utils.wadl.WADLException {
		try{
			if(this.xmlUtils==null){
				throw new Exception("XMLUtils not initialized in WADLUtilities, use static instance 'getInstance(AbstractXMLUtils xmlUtils)'");
			}

			if(!this.xmlUtils.isDocument(wadl)){
				return false;
			}
			Document docXML = this.xmlUtils.newDocument(wadl);
			Element elemXML = docXML.getDocumentElement();
			return isWADL(elemXML);
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException(e.getMessage(),e);
		}
	}
	public boolean isWADL(Document wadl) throws org.openspcoop2.utils.wadl.WADLException {
		Element elemXML = wadl.getDocumentElement();
		return isWADL(elemXML);
	}

	public boolean isWADL(Element wadl) throws org.openspcoop2.utils.wadl.WADLException {
		return isWADL((Node)wadl);
	}
	public boolean isWADL(Node wadl) throws org.openspcoop2.utils.wadl.WADLException {
		try{
			if(wadl == null){
				throw new Exception("Documento wadl da verificare non definito");
			}
			//System.out.println("LOCAL["+wadl.getLocalName()+"]  NAMESPACE["+wadl.getNamespaceURI()+"]");
			if(!"application".equals(wadl.getLocalName())){
				return false;
			}
			if(!"http://wadl.dev.java.net/2009/02".equals(wadl.getNamespaceURI())){
				return false;
			}
			return true;
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException(e.getMessage(),e);
		}
	}




	// TARGET NAMESPACE

	public String getTargetNamespace(byte[]xsd) throws org.openspcoop2.utils.wadl.WADLException {
		try{
			if(this.xmlUtils==null){
				throw new Exception("XMLUtils not initialized in WADLUtilities, use static instance 'getInstance(AbstractXMLUtils xmlUtils)'");
			}

			if(!this.xmlUtils.isDocument(xsd)){
				throw new Exception("Wadl non e' un documento valido");
			}
			Document docXML = this.xmlUtils.newDocument(xsd);
			Element elemXML = docXML.getDocumentElement();
			return getTargetNamespace(elemXML);
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException(e.getMessage(),e);
		}
	}

	public String getTargetNamespace(Document xsd) throws org.openspcoop2.utils.wadl.WADLException {
		Element elemXML = xsd.getDocumentElement();
		return getTargetNamespace(elemXML);
	}

	public String getTargetNamespace(Element elemXML) throws org.openspcoop2.utils.wadl.WADLException {
		return getTargetNamespace((Node)elemXML);
	}

	public String getTargetNamespace(Node elemXML) throws org.openspcoop2.utils.wadl.WADLException {
		try{
			if(this.xmlUtils==null){
				throw new Exception("XMLUtils not initialized in WADLUtilities, use static instance 'getInstance(AbstractXMLUtils xmlUtils)'");
			}

			if(elemXML == null){
				throw new Exception("Wadl non e' un documento valido");
			}
			//System.out.println("LOCAL["+elemXML.getLocalName()+"]  NAMESPACE["+elemXML.getNamespaceURI()+"]");
			if(!"application".equals(elemXML.getLocalName())){
				throw new Exception("Root element non e' un application wadl ("+elemXML.getLocalName()+")");
			}
			String targetNamespace = this.xmlUtils.getAttributeValue(elemXML, "targetNamespace");
			//System.out.println("TARGET["+targetNamespace+"]");
			return targetNamespace;
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException(e.getMessage(),e);
		}
	}

















	/* ------------------ LETTURA---------------------------- */

	/**
	 * Legge un WADL dal path in parametro e ne ritorna la rappresentazione a oggetti.
	 * @param file Il file da cui leggere il WADL.
	 * @return L'oggetto o null in caso di path mancante o errori.
	 */
	public ApplicationWrapper readWADLFromFile(Logger log,File file) throws WADLException{
		return readWADLFromFile(log, file, false, true, true);
	}
	public ApplicationWrapper readWADLFromFile(Logger log, File file,boolean verbose,boolean processInclude, boolean processInlineSchema) throws WADLException{
		return this.readWADLFromFile(log, file, verbose, processInclude, processInlineSchema, null);
	}
	public ApplicationWrapper readWADLFromFile(Logger log, File file,boolean verbose,boolean processInclude, boolean processInlineSchema, 
			Map<String, byte[]> xsdSchemas) throws WADLException{
		try{
			if (file == null) throw new Exception("Path non definito");
			WADLReader reader = getWADLReader(log, verbose, processInclude, processInlineSchema);
			if(xsdSchemas!=null && xsdSchemas.size()>0) {
				for (String name : xsdSchemas.keySet()) {
					byte[] content = xsdSchemas.get(name);
					reader.addSchema(name, content);
				}
			}
			ApplicationNode appNode = reader.readWADL(file.getAbsolutePath());
			ApplicationWrapper ap = new ApplicationWrapper(appNode, reader.getResources(), reader.getMappingNamespaceLocations(), this.xmlUtils);
			return ap;
		}catch(Exception e){
			throw new WADLException("Lettura del wadl non riuscita (File): "+e.getMessage(),e);
		}
	}
	/**
	 * Legge un WADL dal path in parametro e ne ritorna la rappresentazione a oggetti.
	 * @param path Il path da cui leggere il WADL.
	 * @return La definition corretta o null in caso di path mancante o errori.
	 */
	public ApplicationWrapper readWADLFromLocation(Logger log,String path) throws WADLException{
		return readWADLFromLocation(log, path, false, true, true);
	}
	public ApplicationWrapper readWADLFromLocation(Logger log, String path,boolean verbose,boolean processInclude, boolean processInlineSchema) throws WADLException{
		return this.readWADLFromLocation(log, path, verbose, processInclude, processInlineSchema, null);
	}
	public ApplicationWrapper readWADLFromLocation(Logger log, String path,boolean verbose,boolean processInclude, boolean processInlineSchema, 
			Map<String, byte[]> xsdSchemas) throws WADLException{
		try{
			if (path == null) 
				throw new Exception("Path non definito");
			WADLReader reader = getWADLReader(log, verbose, processInclude, processInlineSchema);
			if(xsdSchemas!=null && xsdSchemas.size()>0) {
				for (String name : xsdSchemas.keySet()) {
					byte[] content = xsdSchemas.get(name);
					reader.addSchema(name, content);
				}
			}
			ApplicationNode appNode = reader.readWADL(path);
			ApplicationWrapper ap = new ApplicationWrapper(appNode, reader.getResources(), reader.getMappingNamespaceLocations(), this.xmlUtils);
			return ap;
		}catch(Exception e){
			throw new WADLException("Lettura del wadl non riuscita (Path): "+e.getMessage(),e);
		}
	}
	/**
	 * Legge un WADL dal path in parametro e ne ritorna la rappresentazione a oggetti.
	 * @param wadl I bytes da cui leggere il WADL.
	 * @return La definition corretta o null in caso di path mancante o errori.
	 */
	public ApplicationWrapper readWADLFromBytes(Logger log, byte[] wadl) throws WADLException{
		return readWADLFromBytes(log, wadl, false, true, true);
	}
	public ApplicationWrapper readWADLFromBytes(Logger log, byte[] wadl,boolean verbose,boolean processInclude, boolean processInlineSchema) throws WADLException{
		return this.readWADLFromBytes(log, wadl, verbose, processInclude, processInlineSchema, null);
	}
	public ApplicationWrapper readWADLFromBytes(Logger log, byte[] wadl,boolean verbose,boolean processInclude, boolean processInlineSchema, 
			Map<String, byte[]> xsdSchemas) throws WADLException{
		try{
			if (wadl == null) 
				throw new Exception("Bytes non definiti");
			File tmp = FileSystemUtilities.createTempFile("wadl", ".tmp");
			try {
				FileSystemUtilities.writeFile(tmp, wadl);
				return this.readWADLFromFile(log, tmp, verbose, processInclude, processInlineSchema, xsdSchemas);
			} catch (WADLException e) {
				throw e;
			}finally{
				if(!tmp.delete()) {
					// ignore
				}
			}
		}catch(Exception e){
			throw new WADLException("Lettura del wadl non riuscita (byte[]): "+e.getMessage(),e);
		}
	}
	/**
	 * Legge un WADL dal path in parametro e ne ritorna la rappresentazione a oggetti.
	 * @param doc Document da cui leggere il WADL.
	 * @return La definition corretta o null in caso di path mancante o errori.
	 */
	public ApplicationWrapper readWADLFromDocument(Logger log,Document doc) throws WADLException{
		return readWADLFromDocument(log, doc, false, true, true);
	}
	public ApplicationWrapper readWADLFromDocument(Logger log, Document doc,boolean verbose,boolean processInclude, boolean processInlineSchema) throws WADLException{
		return this.readWADLFromDocument(log, doc, verbose, processInclude, processInlineSchema, null);
	}
	public ApplicationWrapper readWADLFromDocument(Logger log, Document doc,boolean verbose,boolean processInclude, boolean processInlineSchema,
			Map<String, byte[]> xsdSchemas) throws WADLException{
		try{
			if(this.xmlUtils==null){
				throw new Exception("XMLUtils not initialized in WADLUtilities, use static instance 'getInstance(AbstractXMLUtils xmlUtils)'");
			}
			if (doc == null) 
				throw new Exception("Document non definito");
			try {
				byte[]wadl = this.xmlUtils.toByteArray(doc);
				return this.readWADLFromBytes(log, wadl, verbose, processInclude, processInlineSchema, xsdSchemas);
			} catch (WADLException e) {
				throw e;
			}
		}catch(Exception e){
			throw new WADLException("Lettura del wadl non riuscita (Document): "+e.getMessage(),e);
		}
	}
	/**
	 * Legge un WADL dal path in parametro e ne ritorna la rappresentazione a oggetti.
	 * @param elem Element da cui leggere il WADL.
	 * @return La definition corretta o null in caso di path mancante o errori.
	 */
	public ApplicationWrapper readWADLFromDocument(Logger log,Element elem) throws WADLException{
		return readWADLFromDocument(log, elem, false, true, true);
	}
	public ApplicationWrapper readWADLFromDocument(Logger log, Element elem,boolean verbose,boolean processInclude, boolean processInlineSchema) throws WADLException{
		return this.readWADLFromDocument(log, elem, verbose, processInclude, processInlineSchema, null);
	}
	public ApplicationWrapper readWADLFromDocument(Logger log, Element elem,boolean verbose,boolean processInclude, boolean processInlineSchema,
			Map<String, byte[]> xsdSchemas) throws WADLException{
		try{
			if(this.xmlUtils==null){
				throw new Exception("XMLUtils not initialized in WADLUtilities, use static instance 'getInstance(AbstractXMLUtils xmlUtils)'");
			}
			if (elem == null) 
				throw new Exception("Element non definito");
			try {
				byte[]wadl = this.xmlUtils.toByteArray(elem);
				return this.readWADLFromBytes(log, wadl, verbose, processInclude, processInlineSchema, xsdSchemas);
			} catch (WADLException e) {
				throw e;
			}
		}catch(Exception e){
			throw new WADLException("Lettura del wadl non riuscita (Document): "+e.getMessage(),e);
		}
	}
	/**
	 * Legge un WADL dal path in parametro e ne ritorna la rappresentazione a oggetti.
	 * @param uri La URI da cui leggere il WADL.
	 * @return La definition corretta o null in caso di path mancante o errori.
	 */
	public ApplicationWrapper readWADLFromURI(Logger log,URI uri) throws WADLException{
		return readWADLFromURI(log, uri, false, true, true);
	}
	public ApplicationWrapper readWADLFromURI(Logger log, URI uri, boolean verbose,boolean processInclude, boolean processInlineSchema) throws WADLException{
		return this.readWADLFromURI(log, uri, verbose, processInclude, processInlineSchema, null);
	}
	public ApplicationWrapper readWADLFromURI(Logger log, URI uri, boolean verbose,boolean processInclude, boolean processInlineSchema,
			Map<String, byte[]> xsdSchemas) throws WADLException{
		try{
			if (uri == null) 
				throw new Exception("URI non definita");
			WADLReader reader = getWADLReader(log, verbose, processInclude, processInlineSchema);
			if(xsdSchemas!=null && xsdSchemas.size()>0) {
				for (String name : xsdSchemas.keySet()) {
					byte[] content = xsdSchemas.get(name);
					reader.addSchema(name, content);
				}
			}
			ApplicationNode appNode = reader.readWADL(uri);
			ApplicationWrapper ap = new ApplicationWrapper(appNode, reader.getResources(), reader.getMappingNamespaceLocations(), this.xmlUtils);
			return ap;
		}catch(Exception e){
			throw new WADLException("Lettura del wadl non riuscita (URI): "+e.getMessage(),e);
		}
	}













	/* ---------------- METODI GET ------------------------- */

	public Node getIfExistsApplicationElementIntoWADL(Document wadl) throws org.openspcoop2.utils.wadl.WADLException{

		try{
			NodeList list = wadl.getChildNodes();
			if(list!=null){
				for(int i=0; i<list.getLength(); i++){
					Node child = list.item(i);
					if("application".equals(child.getLocalName())){
						return child;
					}
				}
			}
			return null;
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException("Riscontrato errore durante la lettura del wadl: "+e.getMessage(),e);
		}
	}

	public Node getIfExistsGrammarsElementIntoWADL(Document wadl) throws org.openspcoop2.utils.wadl.WADLException{

		try{
			NodeList list = wadl.getChildNodes();
			if(list!=null){
				for(int i=0; i<list.getLength(); i++){
					Node child = list.item(i);
					if("application".equals(child.getLocalName())){
						NodeList listDefinition = child.getChildNodes();
						if(listDefinition!=null){
							for(int j=0; j<listDefinition.getLength(); j++){
								Node childDefinition = listDefinition.item(j);
								if("grammars".equals(childDefinition.getLocalName())){
									return childDefinition;
								}
							}
						}
					}
				}
			}
			return null;
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException("Riscontrato errore durante la lettura del wadl: "+e.getMessage(),e);
		}
	}








	/* ---------------- METODI READ IMPORTS ------------------------- */

	public List<Node> readIncludes(Document wadl) throws org.openspcoop2.utils.wadl.WADLException{

		try{
			List<Node> includes = new ArrayList<Node>();

			NodeList list = wadl.getChildNodes();
			if(list!=null){
				for(int i=0; i<list.getLength(); i++){
					Node child = list.item(i);
					if("application".equals(child.getLocalName())){
						NodeList listDefinition = child.getChildNodes();
						if(listDefinition!=null){
							for(int j=0; j<listDefinition.getLength(); j++){
								Node childDefinition = listDefinition.item(j);
								if("grammars".equals(childDefinition.getLocalName())){
									NodeList listGrammars = childDefinition.getChildNodes();
									if(listGrammars!=null){
										for (int k = 0; k < listGrammars.getLength(); k++) {
											Node childGrammar = listGrammars.item(k);
											if("include".equals(childGrammar.getLocalName())){
												includes.add(childGrammar);
											}
										}
									}
								}
							}
						}
					}
				}
			}
			return includes;
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException("Riscontrato errore durante la lettura del wadl: "+e.getMessage(),e);
		}
	}
	
	
	
	
	
	
	
	
	
	
	
	/* ---------------- METODI REMOVE IMPORTS ------------------------- */
	
	public List<Node> removeIncludes(Document wadl) throws org.openspcoop2.utils.wadl.WADLException{
		
		try{
			List<Node> includes = new ArrayList<Node>();
			
			NodeList list = wadl.getChildNodes();
			if(list!=null){
				for(int i=0; i<list.getLength(); i++){
					Node child = list.item(i);
					if("application".equals(child.getLocalName())){
						NodeList listDefinition = child.getChildNodes();
						if(listDefinition!=null){
							for(int j=0; j<listDefinition.getLength(); j++){
								Node childDefinition = listDefinition.item(j);
								if("grammars".equals(childDefinition.getLocalName())){
									NodeList listGrammars = childDefinition.getChildNodes();
									if(listGrammars!=null){
										for (int k = 0; k < listGrammars.getLength(); k++) {
											Node childGrammar = listGrammars.item(k);
											if("include".equals(childGrammar.getLocalName())){
												includes.add(childGrammar);
												childDefinition.removeChild(childGrammar);
											}
										}
									}
								}
							}
						}
					}
				}
			}
			return includes;
		}catch(Exception e){
			throw new org.openspcoop2.utils.wadl.WADLException("Riscontrato errore durante la lettura del wadl: "+e.getMessage(),e);
		}
	}
	
	
	





	/* ---------------- METODI NAVIGAZIONE WADL ------------------------- */

	public static ResourceNode findResourceNode(ApplicationNode application,String url) throws WADLException{
		if(application == null)
			throw new WADLException("ApplicationNode non fornita");
		return findResourceNode(application.getResources(), url);
	}

	private static ResourceNode findResourceNode(List<ResourceNode> resources,String url) throws WADLException{


		String baseURI = null;

		for (int i = 0; i< resources.size() && baseURI == null; i++) {

			ResourceNode resourceNode = resources.get(i);

			if(baseURI == null && resourceNode.getAllResourceUriTemplate().equals("/")) { //significa che siamo nel primo nodo, che ci serve per ricavare la baseURI
				baseURI = resourceNode.getUriTemplate();
			}

		}

		String[] urlList = extractUrlList(baseURI, url);

		return getResourceNode(urlList, 0, resources);
	}

	private static String[] extractUrlList(String baseURI, String url) throws WADLException{
		if(url == null)
			throw new WADLException("URL non fornita");

		List<String> urlList = new ArrayList<>();

		if(baseURI != null) {
			urlList.add(baseURI);
			if(url.startsWith(baseURI)) {
				url = url.substring(baseURI.length(), url.length());
			}
		}

		for(String s : url.split("/")) {
			if(!s.equals("")) {
				urlList.add("/"+s);
			}
		}

		return urlList.toArray(new String[] {});

	}

	public static boolean isTemplate(String partialURL) {
		String realPartialUrl = partialURL;
		if(realPartialUrl.startsWith("/")) {
			realPartialUrl = realPartialUrl.substring(1);
		}
		return realPartialUrl.startsWith("$");
	}
	
	public static WADLOperation findOperation(List<WADLOperation> operations, String url, HTTPMethods method) throws WADLException {
		String[] urlSplit = extractUrlList(null, url);
		for(WADLOperation op: operations) {
			String[] urlList = extractUrlList(null, op.getPath());
			List<WADLOperation> lst = new ArrayList<WADLOperation>();
			WADLOperation root =  getTreeWadlOperation(op, urlList[0]);
			lst.add(root);
			
			for (int i = 1; i < urlList.length; i++) {
				WADLOperation wadlOp = getTreeWadlOperation(root, urlList[i]);
				lst.add(wadlOp);
			}
			WADLOperation found = getWADLOperation(urlSplit, 0, lst, method);
			if(found != null) {
				return found;
			}
		}
		throw new WADLException("Risorsa ["+url+"] non definita");
	}
	
	private static WADLOperation getTreeWadlOperation(WADLOperation father, String url) {
		WADLOperation child = new WADLOperation();
		child.setMethod(father.getMethod());
		child.setName(father.getName());
		child.setPath(url);
		child.setTemplate(isTemplate(url));
		return child;
	}

	private static WADLOperation getWADLOperation(String[] url, int index, List<WADLOperation> operations, HTTPMethods method) throws WADLException{
		
		if(url.length != operations.size()) { // Non e' sicuramente l'operation giusta
			return null;
		}
		
		WADLOperation operationFound = null;

		if(method.equals(operations.get(index).getMethod())) {
			if(operations.get(index).getPath().equals(url[index])) { //risorsa precisa
				operationFound = operations.get(index);
			} else if(operations.get(index).isTemplate()) { //risorsa template
				operationFound = operations.get(index);
			}
		}

		if(operationFound != null) {
			if(index < url.length-1) {
				WADLOperation rn = getWADLOperation(url, index+1, operations, method);
				if(rn != null) {
					return rn;
				}
			} else {
				return operationFound;
			}
		}

		return null;

	}	

	private static ResourceNode getResourceNode(String[] url, int index, List<ResourceNode> resources) throws WADLException{
		ResourceNode resourceFound = null;
		for(int i = 0; i< resources.size() && resourceFound == null; i++) {
			ResourceNode resourceNode = resources.get(i);

			if(resourceNode.getPathSegment().getTemplate().equals(url[index])) { //risorsa precisa
				resourceFound = resourceNode;
			} else if(resourceNode.getPathSegment().getTemplateParameters().size() > 0) { //risorsa template
				resourceFound = resourceNode;
			}

		}
		if(resourceFound != null) {
			if(index < url.length-1) {
				ResourceNode rn = getResourceNode(url, index+1, resourceFound.getChildResources());
				if(rn != null) {
					return rn;
				}
			} else {
				return resourceFound;
			}
		} else {
			String urlString = "";

			for(String s: url) {
				urlString += s;
			}

			throw new WADLException("Risorsa ["+urlString+"] non definita");
		}

		return null;

	}

	public static MethodNode findMethodNode(ResourceNode resourceNode,HttpRequestMethod httpMethod) throws org.openspcoop2.utils.wadl.WADLException{

		if(httpMethod == null)
			throw new WADLException("Metodo HTTP da cercare non fornito");

		for(MethodNode method: resourceNode.getMethods()) {
			if(httpMethod.name().equalsIgnoreCase(method.getName())) {
				return method;
			}
		} 

		throw new WADLException("Metodo ["+httpMethod+"] non definito per la risorsa ["+resourceNode.getAllResourceUriTemplate()+"]");
	}


}