/**
 * @(#)ResourceMappingScript.java
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 * RID-3 : Resource 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	java.util.*;
import	jp.co.nri.rid.error.*;

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

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

public class ResourceMappingScript implements iMappingScript ,iExceptionCode{
	private static final String	RESOURCEUNIT	= "ResourceUnit";
	private static final String	ZONEUNIT		= "ZoneUnit";
	private static final String	LINKEDPAGEUNIT	= "LinkedPageUnit";
	private static final String	LINKEDPAGE		= "LinkedPage";
	private static final String	KEY				= "Key";
	private static final String	NAME			= "Name";
	private static final String	REFERENCE		= "Reference";
	private static final String	RESOURCE		= "Resource";
	private static final String	PAGE			= "Page";
	private static final String	WIDTH			= "Width";
	private static final String	HEIGHT			= "Height";
	private static final String	ALT				= "Alt";
	private static final String	LINKTYPE		= "LinkType";
	private static final String	CREATOR			= "Creator";
	private static final String	ANALIZER		= "analyzer";
	private static final String	EXIST			= "Exist";
	private static final String	FALSE			= "false";
	private static final String	TRUE			= "true";
	
	private static final String	IMG			= "img";
	private static final String	EMBED		= "embed";
	private static final String	INPUT		= "input";
	private static final String	BODY		= "body";
	private static final String	TABLE		= "table";
	private static final String	TR			= "tr";
	private static final String	TD			= "td";
	private static final String	SCRIPT		= "script";
	private static final String	LINK		= "link";
	private static final String	INCLUDE		= "include";
	private static final String	EXEC		= "exec";
	private static final String	SRC			= "src";
	private static final String	LOWSRC		= "lowsrc";
	private static final String	BACKGROUND	= "background";
	private static final String	FILE		= "file";
	private static final String	VIRTUAL		= "virtual";
	private static final String	CMD			= "cmd";
	private static final String	CGI			= "cgi";
	private static final String	HREF		= "href";
	private static final String	IMGSRC		= "img.src";
	private static final String	IMGLOWSRC	= "img.lowsrc";
	private static final String	EMBEDSRC	= "embed.src";
	private static final String	EMBEDLOWSRC	= "embed.lowsrc";
	private static final String	INPUTSRC	= "input.src";
	private static final String	INPUTLOWSRC	= "input.lowsrc";
	
	private static final String[]	names = { IMG ,EMBED ,INPUT ,BODY ,TABLE ,TR ,TD, SCRIPT, LINK };
	private static final String[][]	attrmap ={
										 {IMG	,SRC	,LOWSRC	}
										,{EMBED	,SRC	,LOWSRC	}
										,{INPUT	,SRC	,LOWSRC	}
										,{BODY	,BACKGROUND	}
										,{TABLE	,BACKGROUND	}
										,{TR	,BACKGROUND	}
										,{TD	,BACKGROUND	}
										,{INCLUDE	,FILE	,VIRTUAL}
										,{EXEC		,CMD	,CGI	}
										,{SCRIPT	,SRC	}
										,{LINK		,HREF	,SRC}
									};
	private static final String[][]	optionmap ={
										 {IMGSRC			,WIDTH	,HEIGHT	,ALT}
										,{IMGLOWSRC			,WIDTH	,HEIGHT	,ALT}
										,{EMBEDSRC			,WIDTH	,HEIGHT	,ALT}
										,{EMBEDLOWSRC		,WIDTH	,HEIGHT	,ALT}
										,{INPUTSRC			,WIDTH	,HEIGHT	,ALT}
										,{INPUTLOWSRC		,WIDTH	,HEIGHT	,ALT}
									};
	
//	private elementBooster	myLinkedPage;
	private elementBooster	myTopZone;
//	private WKeyMap			myLinkedPages;
	private StringMap2D		myTagMap;
	private StringMap2D		myOptionMap;
	private BaseURL			baseURL;
	private LinkedPageList	linkedPageList;
	
//--------[[ constructor ]]----------------------------------------------------
	/**
	 * @param	root	MappingするXMLのルートElement
	 * @see		elementBooster
	 */
	public ResourceMappingScript(elementBooster root, LinkedPageList linkedPageList, BaseURL baseURL){
		this.baseURL		= baseURL;
		this.linkedPageList	= linkedPageList;
		
		myTagMap	= new StringMap2D(attrmap);
		myOptionMap = new StringMap2D(optionmap);
		myTopZone = root.child(ZONEUNIT);
		
		// LinkedPageUnit
/*
		myLinkedPage = root.child(LINKEDPAGE);
		if(null==myLinkedPage)	myLinkedPage = root.add(LINKEDPAGE);
		myLinkedPages = new WKeyMap(PAGE ,RESOURCE ,myLinkedPage.children(LINKEDPAGEUNIT));
*/
	}
	
//--------[[ open method ]]----------------------------------------------------
	/**
	 * このScriptがcatchしたいattribute。(resource系)
	 * @return	attribute名配列(img.src ,img.lowsrc ,embed.src)
	 * @since	1.00
	 */
	public String[] names(){ return names; }
	
	/**
	 * resouce系attributeにhitしたときに起動される。
	 * @param	file	HTMLファイル名
	 * @param	name	attribute名
	 * @param	text	attribute値
	 */
	public void hit(String file ,String name ,elementBooster element)
		throws RidException ,RWarningException
	{
		RWarningException warnings = new RWarningException();
		
		String[] attributes = myTagMap.get(name);
		int mx = attributes.length;
		for(int i=0;i<mx;i++){
			try{ resource(file ,element ,element.attribute(attributes[i]) ,element.tagname()+"."+attributes[i]); }
			catch(RWarningException e){ warnings.add(e); }
		}
		
		if(0<warnings.length())	throw warnings;
	}
	
	public void hitForFlash(String fromPage ,String value ,String linktype)
		throws RidException ,RWarningException
	{
		RWarningException warnings = new RWarningException();
		
			try{ resource(fromPage ,null ,value ,linktype); }
			catch(RWarningException e){ warnings.add(e); }
		
		if(0<warnings.length())	throw warnings;
	}

//	private void resource(String file ,String name ,elementBooster element ,String text ,String linktype)
	private void resource(String file ,elementBooster element ,String text ,String linktype)
	throws RWarningException,RidException
	{
		String origText = text;

		if(null==text)	return;
		
		// フルパスURL -> 相対パス
		if(null != baseURL){
			String innerPath = baseURL.getInnerPath(file, text);
			if(null != innerPath) text = innerPath;
		}
		
		text = FileNameStudio.cutParamForUrlUnit(text);
		
		// Full URL ?
		if(FileNameStudio.isURL(text))		return;
		
		// Ignore File
		if(RIgnoreFiles.ignorefile(text))	return;
		
		// file name
		String filename = FileNameStudio.filename(text);
		if(null==filename) {
			throw new RWarningException(WAR_BadResourceName,
										"\"" + origText + "\"" + " (from "
										+ file + ")");
		}
		
		// zone name
		String basezone = text.startsWith("/") ? "/" : FileNameStudio.zonename(file);
		String resourcezone	= FileNameStudio.zonename(basezone ,FileNameStudio.zonename(text));
		
		// zone + file -> "File" attribute
		String resourcefile = null;
		if(null==resourcezone)	resourcefile = new String(filename);
		else	resourcefile = resourcezone+"/"+filename;
		
		// element add
		
		//zone resourcezoneを加工
//		elementBooster elem = FileNameStudio.unit(myTopZone ,RESOURCEUNIT ,KEY ,resourcefile);	// ++++
		Element elem = findResourceUnit(resourcefile, myTopZone.element());
		
		if(null==elem){
			Element zoneElem = findZoneUnit(resourcefile, myTopZone.element());

			/* すでに同名の ZoneUnit が存在すれば、同名の
			   ResourceUnit は作らない */
			if (zoneElem != null) {
				throw new RWarningException(WAR_ResourceUnitCreationFail ,resourcefile);
			}

			/* ZoneUnit の生成に失敗したら、その Zone の下には
			   ResourceUnit を作らない */
                        elementBooster zone = FileNameStudio.zone(myTopZone ,resourcefile ,ANALIZER);
			if (zone == null) {
				throw new RWarningException(WAR_ZoneUnitCreationFail ,resourcefile);
			}

			elem = zone.add(RESOURCEUNIT).element();
			elem.setAttribute(NAME		,filename);
			elem.setAttribute(CREATOR	,ANALIZER);
			elem.setAttribute(EXIST		,FALSE);
		}
		
		// LinkedPageUnit
//		elementBooster linkedpage = myLinkedPages.element(file ,resourcefile);
		Element linkedpage = linkedPageList.get(resourcefile);

		if(null==linkedpage){
			linkedpage = linkedPageList.appendUnit(resourcefile);
			
/*
			linkedpage = myLinkedPage.add(LINKEDPAGEUNIT);
			linkedpage.attribute(PAGE		,file);
			linkedpage.attribute(RESOURCE	,resourcefile);
			myLinkedPages.add(linkedpage);
*/
		}
		
		linkedpage.setAttribute(LINKTYPE	,linktype);
		
		String[] options = myOptionMap.get(linktype);
		if(null==options)	return;
		
		int mx = options.length;
		for(int i=0;i<mx;i++){
			linkedpage.setAttribute(options[i]	,element.attribute(options[i].toLowerCase()));
		}
	}
	
	private Element findZoneResourceUnit(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) && !RESOURCEUNIT.equals(nodename)) continue;
			
			Element elm = (Element)n;
			if(name.equalsIgnoreCase(elm.getAttribute(NAME)))	return elm;
		}
		return null;
	}
	
	private Element findResourceUnit(String path, Element zone){
		String[] dirs = path.split("/");
		int len = dirs.length;
		for(int i = 0; i < len;i++){
			NodeList nl = zone.getChildNodes();
			zone = findZoneResourceUnit(dirs[i], nl);
			if(null == zone)	return null;
		}
		
		return zone.getTagName().equals(RESOURCEUNIT) ? zone : null;
	}
	
	private Element findZoneUnit(String path, Element zone){
		String[] dirs = path.split("/");
		int len = dirs.length;
		for(int i = 0; i < len;i++){
			NodeList nl = zone.getChildNodes();
			zone = findZoneResourceUnit(dirs[i], nl);
			if(null == zone)	return null;
		}
		
		return zone.getTagName().equals(ZONEUNIT) ? zone : null;
	}
	
	private class StringMap2D extends HashMap{
		private	String	myKeyName1;
		private	String	myKeyName2;
	
	public StringMap2D(String[][] strings){
		int mx = strings.length;
		for(int i=0;i<mx;i++){
			String[] data = strings[i];
			int mx2 = data.length;
			for(int j=1;j<mx2;j++)	add(data[0] ,data[j]);
		}
	}
	
	public void add(String key ,String value){
		Vector vec = (Vector)(super.get(key));
		
		if(null==vec){
			vec = new Vector();
			put(key ,vec);
		}
		vec.add(value);
	}
	
	public String[] get(String key){
		try{ return (String[])((Vector)(super.get(key))).toArray(new String[0]); }
		catch(NullPointerException e){ return null; }
	}
	}
	
}
