/*
 * @(#)HyperLinkMappingScript.java
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 * RID-3 : Hyper Link Mapping Script
 * @author  Intelligent Technology Inc.
 * @version 1.00 2001/10/01-2001/12/28
 * @see 
 */
package	jp.co.nri.rid.analyzer;

import	jp.co.nri.rid.xml.*;
import	jp.co.nri.rid.importer.*;
import	java.net.*;
import	java.io.*;
import jp.co.nri.rid.error.*;

// W3C DOM
import	org.w3c.dom.*;

/**
 * リンク系の属性値を解析するクラス。
 */

public class HyperLinkMappingScript implements iMappingScript, iExceptionCode{
	private static final String[]	names = { "attribute.href" ,"attribute.action" ,"form.action" ,"frame.src" ,"area.href" ,"cfform.action" ,"cflocation.url" ,"cfinclude.template" ,"cferror.template" ,"meta.content","rid.href" };
	private static final String	PAGEUNIT	= "PageUnit";
//	private static final String	DIRECTION	= "Direction";
	private static final String	ZONEUNIT	= "ZoneUnit";
	private static final String	URLUNIT		= "UrlUnit";
//	private static final String	FILE		= "File";
	private static final String	NAME		= "Name";
	private static final String	KEY			= "Key";
	private static final String	EXIST		= "Exist";
	private static final String	TOP			= "Top";
	private static final String	PAGETYPE	= "PageType";
//	private static final String	ZEROTOONE	= "→";
//	private static final String	ONETOZERO	= "←";
//	private static final String	BOTH		= "both";
	private static final String	TRUE		= "true";
	private static final String	FALSE		= "false";
	private static final String	STATIC		= "static";
	private static final String	DYNAMIC		= "dynamic";
	private static final String	SERVER		= "server";
	private static final String	CREATOR		= "Creator";
	private static final String	ANALIZER	= "analyzer";
	private static final String	INTENSITY	= "Intensity";
	private static final String	REAL		= "Real";
	private static final String	METACONTENT	= "meta.content";
	
	private elementBooster	myElement;
	private BaseURL			baseURL;
//	private elementBooster	linkTag;
//	private Element			linkElement;
//	private NodeList		linkNodeList;
	private Document		document;
	private	elementBooster	topZone;
	private	Element			topZoneElement;
	private NodeList		urlUnitNodeList;
	private LinkList		linkList;
//--------[[ constructor ]]----------------------------------------------------
	/**
	 * @param	root	MappingするXMLのルートElement
	 * @see		elementBooster
	 */
	public HyperLinkMappingScript(elementBooster root, LinkList linkList, BaseURL baseURL){
		myElement		= root;
		this.baseURL	= baseURL;
		this.linkList	= linkList;
		document		= root.element().getOwnerDocument();
		topZone			= root.child(ZONEUNIT);
		topZoneElement	= topZone.element();
		
		urlUnitNodeList	= topZoneElement.getElementsByTagName(URLUNIT);
	}
	
//--------[[ open method ]]----------------------------------------------------
	/**
	 * このScriptがcatchしたいattribute。(HyperLink系)
	 * @return	attribute名配列(attribute.href ,attribute.action ,frame.src)
	 * @since	1.00
	 */
	public String[] names(){ return names; }
	
	/**
	 * HyperLink系attributeにhitしたときに起動される。
	 * @param	file	HTMLファイル名
	 * @param	name	attribute名
	 * @param	text	attribute値
	 */
	public void hit(String file ,String name ,elementBooster element)
		throws RidException ,RWarningException
	{
		String text = element.text();
		if(METACONTENT.equals(name))	text = FileNameStudio.getMetaContentLink(text);
		if(null==text)	return;
		
		RWarningException warnings = new RWarningException();
		try {
			// Target
			String target = target(file ,text);
			if(null==target)	return;
			
			// Links
			Element linkunit = linkList.get(target);
			if(null == linkunit){
				linkunit = linkList.appendUnit(target);
				
				if("rid.href".equals(name)){
					linkunit.setAttribute(INTENSITY	,"Virtual");
				} else{
				linkunit.setAttribute(EXIST		,TRUE);
					linkunit.setAttribute(INTENSITY	,REAL);
				}
			} else	linkunit.setAttribute(EXIST ,TRUE);
			
		} catch(RWarningException e) {
			warnings.add(e);
		}
		if(0<warnings.length())	throw warnings;
	}
	
//--------[[ close method ]]----------------------------------------------------
	private String target(String file ,String text) throws RidException,
	RWarningException{
		
		// skip ?
		if(FileNameStudio.skip(text))	return null;
		
		// フルパスURL -> 相対パス
		if(null != baseURL){
			String innerPath = baseURL.getInnerPath(file, text);
			if(null != innerPath) text = innerPath;
		}
		
		// top zone
//		elementBooster topzone = myElement.child(ZONEUNIT);
		
		text = FileNameStudio.cutParamForUrlUnit(text);
		
		// Full URL ?
		if(FileNameStudio.isURL(text)){	// Full URL
			String pureurl = FileNameStudio.pureURL(text);
//			String pureurl = FileNameStudio.pureURLForUrlUnit(text);
			if(null!=pureurl)	pureurl = urlunit(pureurl);
			return pureurl;
		}
		
		// 相対URL
		
		if(RIgnoreFiles.ignorefile(text))	return null;
		
		String fusionurl = null;
		if(text.equals("/"))			fusionurl = null;				// "/"のみ
//		else if(text.startsWith("/"))	fusionurl = text.substring(1);	// "/"で始まっている
		else if(text.startsWith("/")){
			try{ fusionurl = FileNameStudio.fusion("/" ,text); }
			catch(java.lang.ArrayIndexOutOfBoundsException e){
				fusionurl = FileNameStudio.simplefusion("/" ,text);
//				fusionurl = FileNameStudio.simplefusionForUrlUnit("/" ,text);
				if(null!=fusionurl)	fusionurl = urlunit(fusionurl);
				return fusionurl;
			}
			if(null==fusionurl)	return null;
		}
		else{
			try{ fusionurl = FileNameStudio.fusion(file ,text); }
			catch(java.lang.ArrayIndexOutOfBoundsException e){
				fusionurl = FileNameStudio.simplefusion(file ,text);
//				fusionurl = FileNameStudio.simplefusionForUrlUnit(file ,text);
				if(null!=fusionurl)	fusionurl = urlunit(fusionurl);
				return fusionurl;
			}
			if(null==fusionurl)	return null;
		}
		
		// /で終わっている
		String pagename = null;
		try{ pagename = FileNameStudio.page(fusionurl); }
		catch(java.util.NoSuchElementException e){}
		if(null==pagename){
			return defaultfile(topZone ,fusionurl); // Default FileName
		}
		
		
/*
		// とりあえずPage検索
		if(null!=FileNameStudio.unit(topZone ,PAGEUNIT ,KEY ,fusionurl))	return fusionurl;
		
		// とりあえずZone検索
		if(null!=FileNameStudio.unit(topZone ,ZONEUNIT ,KEY ,fusionurl))	return defaultfile(topZone ,fusionurl);
*/
		Element unit = findZonePageUnit(fusionurl, topZoneElement);
		if(null != unit){
			if(unit.getTagName().equals(PAGEUNIT))	return fusionurl;
			if(unit.getTagName().equals(ZONEUNIT))	return defaultfile(topZone ,fusionurl);
		}
		
		// 拡張子判断
			// 拡張子が無い=ZoneUnit zone/index.html作成(exists="false")
			// ページ系=PageUnit (exists="false")
			// リソース系=無視
			// それ以外=CGI(exists="false" ,PageType="server")
		if(FileNameStudio.isZone(pagename))		return defaultfile(topZone ,fusionurl);
		if(FileNameStudio.isResource(pagename))	return null;	// Resource系はskip
		
//		elementBooster unit = pageunit(FileNameStudio.zone(topZone ,fusionurl ,ANALIZER) ,pagename);
		elementBooster parentZoneUnit = FileNameStudio.zone(topZone ,fusionurl ,ANALIZER);
		/* ZoneUnit の生成に失敗したら、その Zone の下には PageUnit を
		   作らない */
		if (parentZoneUnit == null) {
			throw new RWarningException(WAR_ZoneUnitCreationFail, fusionurl);
		}
		pageunit(parentZoneUnit ,pagename);
		return fusionurl;
	}
	
	private String defaultfile(elementBooster topzone ,String url) throws RidException, RWarningException{
		String zone = null;
		
		// ZoneUnitなし zone/index.html作成(exists="false")
		elementBooster	zoneunit = null;
		if(null==url)	zoneunit = topzone;
		else{
			if(url.endsWith("/"))	zone = url.substring(0,url.length()-1);
			else					zone = new String(url);
			zoneunit = FileNameStudio.unit(topzone ,ZONEUNIT ,KEY ,zone);
			
			if(null==zoneunit){
				String fullurl = new StringBuffer(zone).append("/index.html").toString();
				elementBooster parentZoneUnit = FileNameStudio.zone(topzone ,fullurl ,ANALIZER);
				/* ZoneUnit の生成に失敗したら、その Zone の下には PageUnit を
				   作らない */
				if (parentZoneUnit == null) {
					throw new RWarningException(WAR_ZoneUnitCreationFail ,fullurl);
				}
				pageunit(parentZoneUnit ,"index.html");
				return fullurl;
			}
		}
		// ZoneUnitあり
		//zone検索 TOP-PAGE -> index.html -> index.htm -> index.html作成(exists="false")
		// "TOP" attribute
		String top = zoneunit.attribute(TOP);
		if(null!=top){
			if(RIgnoreFiles.ignorename(top))	return null;
			
			String fullurl = null;
			if(null==zone)	fullurl = top;
			else			fullurl = new StringBuffer(zone).append("/").append(top).toString();
			if(null==FileNameStudio.unit(topzone ,PAGEUNIT ,KEY ,fullurl))	pageunit(zoneunit ,top);	// PageUnitなし
			
			return fullurl;
		}
		
		// "TOP" attributeなし
		String fullurl = null;
		if(null==zone)	fullurl = "index.html";
		else			fullurl = new StringBuffer(zone).append("/index.html").toString();
		if(null!=FileNameStudio.unit(topzone ,PAGEUNIT ,KEY ,fullurl))	return fullurl;	// PageUnitあり
		
		// PageUnit(index.html)なし
		if(null==zone)	fullurl = "index.htm";
		else			fullurl = new StringBuffer(zone).append("/index.htm").toString();
		if(null!=FileNameStudio.unit(topzone ,PAGEUNIT ,KEY ,fullurl))	return fullurl;	// PageUnitあり
		
		// PageUnit(index.htm)なし
		if(null==zone)	fullurl = "index.html";
		else			fullurl = new StringBuffer(zone).append("/index.html").toString();
		
		pageunit(zoneunit ,"index.html");
		return fullurl;
	}
	
	private Element findUnit(String name, NodeList unitNodeList){
		for(int i = unitNodeList.getLength() - 1; 0 <= i;i--){
			Element unit = (Element)unitNodeList.item(i);
			
			String value = unit.getAttribute(NAME);
			if(value.equalsIgnoreCase(name))	return unit;
		}
		return null;
	}
	
	private Element findZonePageUnit(String name, NodeList unitNodeList){
		for(int i = unitNodeList.getLength() - 1; 0 <= i;i--){
			Node n = unitNodeList.item(i);
			if(Node.ELEMENT_NODE != n.getNodeType())	continue;
			
			String nodename = n.getNodeName();
			if(!ZONEUNIT.equals(nodename) && !PAGEUNIT.equals(nodename)) continue;
			
			Element elm = (Element)n;
			if(name.equalsIgnoreCase(elm.getAttribute(NAME)))	return elm;
		}
		return null;
	}
	
	private Element findZonePageUnit(String path, Element zone){
		String[] dirs = path.split("/");
		int len = dirs.length;
		for(int i = 0; i < len;i++){
			NodeList nl = zone.getChildNodes();
			zone = findZonePageUnit(dirs[i], nl);
			if(null == zone)	return null;
		}
		
		return zone;
	}
	
	private String urlunit(String url){
		try { url = new URI(url).normalize().toString(); }
		catch(URISyntaxException use){}
		
/*
		elementBooster element = FileNameStudio.unit(topZone ,URLUNIT ,NAME ,url);
		
		if(null==element){
			element = topZone.add(URLUNIT);
			element.attribute(NAME ,url);
			element.attribute(CREATOR	,ANALIZER);
		}
*/
		Element element = findUnit(url, urlUnitNodeList);
		
		if(null==element){
			element	= document.createElement(URLUNIT);
			element.setAttribute(NAME		,url);
			element.setAttribute(CREATOR	,ANALIZER);
			topZoneElement.appendChild(element);
		}
		
		return url;
	}
	
	private elementBooster pageunit(elementBooster zone ,String name) throws RidException {
		elementBooster pageunit = zone.add(PAGEUNIT);
//		elementBooster pageunit = zone.newElement(PAGEUNIT);
		pageunit.attribute(NAME		,name);
		pageunit.attribute(EXIST	,FALSE);
		pageunit.attribute(CREATOR	,ANALIZER);
		if(FileNameStudio.isStaticPage(name))		pageunit.attribute(PAGETYPE ,STATIC);
		else pageunit.attribute(PAGETYPE ,DYNAMIC);

		return pageunit;
	}
}
