/*
 * @(#)RidDocumentSite.java
 *
 * Copyright 2001 Nomura Research Institute, LTD. All rights reserved.
 *
 * RID-X : Document
 *
 * @author  Intelligent Technology Inc.
 * @version 1.01 2001/11/07
 */
package jp.co.nri.rid.doc;

import java.awt.Color;
import java.awt.Dimension;

import java.awt.geom.AffineTransform;

import java.util.Collections;
import java.util.Comparator;
import java.util.Hashtable;
import java.util.Enumeration;
import java.util.Vector;

import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import jp.co.iti.fagot.fob.xml.XElementNS;

import jp.co.iti.fagot.ZApp;

import jp.co.iti.fagot.geom.ZPoint;
import jp.co.iti.fagot.geom.ZRectangle;

import jp.co.iti.fagot.gob.GobManager;

import jp.co.iti.fagot.doc.DocElement;
import jp.co.iti.fagot.doc.DocPane;

import jp.co.iti.fagot.util.ZInstance;
import jp.co.iti.fagot.util.ZString;
import jp.co.iti.fagot.util.ZDebug;
import jp.co.iti.fagot.util.ZResource;
import jp.co.iti.fagot.util.ZReverseEnumeration;

import jp.co.nri.Rid;
import jp.co.nri.rid.RUpdateManager;
/**
 * Rid Document(Site用)
 */
public class RidDocumentSite extends RidDocument {

	/**
	 * デフォルトコンストラクタ
	 */
	public RidDocumentSite() {
	}

	//## IDocument ####################################################
	/**
	 * 初期化
	 */
	public void initDocument() {
	super.initDocument();

	ZResource res = ZApp.getTheApp().getResource();

	// 参照ファイル
	Element refElement = createElementNS(null,"ReferenceUnit");

	// リソースファイル
	Element resElement = createElementNS(null,"FileUnit");
	resElement.setAttribute("Type","resource");
	resElement.setAttribute("Name",res.getString("rid.resource.xml"));
	refElement.appendChild(resElement);

	// ドキュメントファイル
	Element docElement = createElementNS(null,"FileUnit");
	docElement.setAttribute("Type","document");
	docElement.setAttribute("Name",res.getString("rid.document.xml"));
	refElement.appendChild(docElement);

	// スナップショットファイル
	Element snpElement = createElementNS(null,"FileUnit");
	snpElement.setAttribute("Type","snapshot");
	snpElement.setAttribute("Name",res.getString("rid.snapshot.xml"));
	refElement.appendChild(snpElement);

	getDocumentElement().appendChild(refElement);

	// トップゾーン
	Element topElement = createElementNS(null,"ZoneUnit");
	topElement.setAttribute("Name","_top");
	getDocumentElement().appendChild(topElement);

	// トップリンク
	Element linkElement = createElementNS(null,"Link");
	getDocumentElement().appendChild(linkElement);

	// トップシナリオ
	Element scenarioElement = createElementNS(null,"Scenario");
	getDocumentElement().appendChild(scenarioElement);

	}

	//## パネルから呼び出されるメソッド ###############################
	/**
	 * 背景色の取得
	 * @param szPanelId パネル識別子
	 */
	public Color getBackground(String szPanelId) {
	ZResource res = ZApp.getTheApp().getResource();

	
	if ( szPanelId != null ) {
		// ゾーンパネルの背景はルートゾーンの色
		if ( szPanelId.equals(Rid.PANEL_ZONE) ) {
			RidElement zone = (RidElement)getRootElement(Rid.PANEL_ZONE);
			return zone.getBackgroundColor();
		} else if ( szPanelId.startsWith(Rid.PANEL_MAP) ) {
			return res.getColor("panel.color."+szPanelId);
		}
	}
	return super.getBackground(szPanelId);
	}

	/**
	 * コンポーネントの論理的な最適サイズを返します
	 * この値は拡大縮小表示の有無にかかわらず固定です。
	 *
	 * getPreferredSizeは拡大表示されると大きくなり、縮小表示されると小さくなります
	 */
	public Dimension getLogicalSize() {
	return new Dimension(8000*Rid.PANELSIZE,6000*Rid.PANELSIZE);
	}

	//## root #########################################################
	/**
	 * 現在ルートの設定(ゾーンパネルのみ使用)
	 * @param szPanelId パネルID
	 * @param elm       ルートエレメント
	 */
	public void setRootElement(String szPanelId, Element elm) {

	if ( elm != null ) {
		mRoot.put(szPanelId,elm);
	} else {
		mRoot.remove(szPanelId);
	}

	// 画面を中央に移動
/*
	String szTitle = ZInstance.prop().getProperty( Rid.PANEL_ACTIVE );
	JComponent active = (JComponent)ZInstance.map().get(szTitle);
	
	if ( !(active instanceof DocPane) ) {
		// アクティブパネルが描画パネルでない場合は描画しない
		return;
	}
	DocPane panel = (DocPane)active;
*/

	// 20020909 パネル表示初期位置は、DocPaneで設定しているので、ここでは設定しない
//	panel.setCenter(new ZPoint(4000*Rid.PANELSIZE,3000*Rid.PANELSIZE));
	}

	/**
	 * 現在ルートの取得
	 *
	 * Zoneパネルでは、現在ゾーンのエレメントを返す
	 * Pageパネルでは、現在ページのエレメントを返す
	 * Mapパネルでは、getDocumentElement()を返す
	 *
	 * @param szPanelId パネル識別子
	 */
	public Element getRootElement(String szPanelId) {
	Element root =(Element)mRoot.get(szPanelId);
	// ルートページが設定済みで削除されていれば、取り消し
	if ( root != null && ((RidElement)root).getStatus().equals("remove") ) {
//		ZDebug.trace();
		mRoot.remove( szPanelId );
	}

	if ( root == null ) {
//		ZDebug.trace();
		root = getTopZone();
	}

	return root;
	}

	/**
	 * トップページの設定(CircleMapのみ使用)
	 * @param szPanelId パネル識別子
	 * @param szFile    トップページのFileアトリビュート
	 */
	public void setTopPage(String szPanelId, String szFile) {
	DocElement root = getTopZone();
	// DOMに格納されているルートエレメントを取得する
	DocElement map = (DocElement)root.getChildElement(".ViewInfo."+szPanelId,null);
	map.setAttribute("Top", szFile);
	}

	/**
	 * トップページの取得
	 *
	 * ページパネル・マップパネルにおいて現在の基準ページを取得する
	 *
	 * @param szPanelId パネル識別子
	 */
	public RidElementPage getTopPage(String szPanelId) {
	RidElementPage topPage = null;
	DocElement root = getTopZone();
	// DOMに格納されているルートエレメントを取得する
	String szTop = root.getChildElementAttribute(".ViewInfo."+szPanelId,null,"Top");
	if ( szTop.length() > 0 ) {
//		topPage = (RidElementPage)root.findChildElement("PageUnit","File="+szTop);
		topPage = (RidElementPage)findChildElementInZone("PageUnit",szTop);

		// DOMに格納されているトップページがない場合は、空に再設定
		if ( topPage == null ) {
			setTopPage( szPanelId,"");
			// トップページがなくなった場合は、アクセスカウントの更新
			requestUpdateAccessCount();
		}
	}
	
	// なければ トップゾーンのTopプロパティを参照する
	if ( topPage == null ) {
		topPage = getDefaultTopPage(szPanelId);
/*
		ZDebug.trace("defaultTop:"+topPage.getAttribute("File"));
	} else {
		ZDebug.trace("Top:"+topPage.getAttribute("File"));
*/
	}
	return topPage;
	}

	/**
	 * デフォルトトップページの取得
	 *
	 * ページパネル・マップパネルにおいてデフォルトの基準ページを取得する
	 *
	 * @param szPanelId パネル識別子
	 */
	public RidElementPage getDefaultTopPage(String szPanelId) {
	RidElementZone root = getTopZone();
	return root.getDefaultTopPage();
	}

	//## MapModel #####################################################
	/**
	 * MapModelの設定
	 * @param szPanelId パネルID
	 * @param model     モデル
	 */
//	public void setMapModel(String szPanelId, MapModel model) {
//	mMapModel.put(szPanelId,model);
//	}

	/**
	 * MapModelの取得
	 * @param szPanelId パネルID
	 */
	public MapModel getMapModel(String szPanelId) {
	MapModel model = (MapModel)mMapModel.get(szPanelId);
	if ( model == null ) {
		Dimension dim = getLogicalSize();
		model = new MapModel();
		if ( szPanelId.endsWith("Site") ) {
			model.setObjectTransform(new AffineTransform(1,0.75,0,1,0,0));
			model.setCoordTransform(new AffineTransform(80,-60,80,60,0,0));
			model.setLinkOffset(new ZPoint(0.5,1));
			model.setBasePoint(new ZPoint(dim.width/3,dim.height*2/3));
		} else if ( szPanelId.endsWith("Hier") ) {
			model.setCoordTransform(new AffineTransform(0,100,100,0,0,0));
			model.setBasePoint(new ZPoint(dim.width/2,300));
		} else if ( szPanelId.endsWith("Circle") ) {
			model.setCoordTransform( null );
			model.setBasePoint(new ZPoint(dim.width/2,dim.height/2));
		}
		mMapModel.put( szPanelId, model );
	}
	return model;
	}

	//## シナリオ #####################################################
	/**
	 * シナリオの作成
	 */
	public void createScenario() {
	ZResource res = ZApp.getTheApp().getResource();

	String szMsg1 = res.getString("msg.askscenarioname1");
	String szMsg2 = res.getString("msg.askscenarioname2");
	JFrame frame  = (JFrame)ZInstance.map().get( Rid.FRAME );
	String result = JOptionPane.showInputDialog(frame,
		szMsg1,"Ridual",JOptionPane.QUESTION_MESSAGE);
	DocElement scene = (DocElement)((DocElement)getDocumentElement()).findChildElement(".Scenario",null);
	while ( result != null && result.length()>0 ) {
		result = ZString.trimLeft( result );
		if ( result.length()==0 ) {
			break;
		}
		XElementNS unit = scene.findChildElement(".ScenarioUnit","Name="+result );
		if ( unit == null ) {
			beginTransaction();

			// シナリオユニットの作成
			unit = (XElementNS)createElementNS(null,"ScenarioUnit");
			unit.setAttribute("Name",result);
			scene.appendChild( unit );

			// 作成したシナリオをカレントシナリオに設定
			setScenarioName( result );

			commit();
			break;
		}
		// 名前が重複しているので、再入力
		result = JOptionPane.showInputDialog(frame,
				result+" "+szMsg2,"Ridual",JOptionPane.WARNING_MESSAGE);
	}
	}

	/**
	 * シナリオの削除
	 */
	public void deleteScenario() {
	// 作成したシナリオをカレントシナリオに設定
	String szName = getScenarioName() ;

	if ( szName.length()>0 ) {
		beginTransaction();
		DocElement scene = (DocElement)((DocElement)getDocumentElement()).findChildElement(".Scenario",null);
		XElementNS unit = scene.findChildElement(".ScenarioUnit","Name="+szName );
		scene.removeChild( unit );
		setScenarioName("");
		commit();
	}
	}

	/**
	 * シナリオ名の設定
	 */
	public void setScenarioName(String szName) {
	DocElement top = getTopZone();
	XElementNS elm = top.getChildElement(".ViewInfo.Map",null );
	elm.setAttribute("Scenario",szName);

	RUpdateManager.update(RUpdateManager.UPDATE_MAP);
	}

	/**
	 * シナリオ名の取得
	 */
	public String getScenarioName() {
	DocElement top = getTopZone();
	XElementNS elm = top.getChildElement(".ViewInfo.Map",null );
	return elm.getAttribute("Scenario");
	}

	/**
	 * シナリオの取得
	 */
	public RidElementScenario getScenario(String szName) {
	RidElementScenario scenario = null;
	if ( szName.length()>0 ) {
		DocElement scene = (DocElement)((DocElement)getDocumentElement()).findChildElement(".Scenario",null);
		scenario = (RidElementScenario)scene.findChildElement(".ScenarioUnit","Name="+szName );
	}
	return scenario;
	}

	//## 描画 #########################################################
	/**
	 * 描画エレメントの取得
	 * @param szPanelId パネル識別子
	 */
	public Vector createGobs(String szPanelId) {
	Vector gobs = new Vector(); ;
	if ( szPanelId.equals(Rid.PANEL_ZONE) ) {
		// Gobを作成する前に全てのエレメントのMainGobを無効にする
		DocElement top = getTopZone();
		top.preCreateGobs( szPanelId ) ;

		// 追加Gobの削除
		// 追加Gobは、createLinkGobsの途中で追加される
		GobManager.list(szPanelId,"additional").removeAllElements();
		RidElement.mAllUnits = null;

		// 描画エレメントの取得
		gobs.addAll( super.createGobs(szPanelId) );

		// リンクエレメントの取得
		Vector links = createLinkGobs( szPanelId );

		// 追加Gobの追加：リンクエレメントの取得中に作成される
		gobs.addAll( GobManager.list(szPanelId,"additional"));

		// リンクエレメントの追加
		gobs.addAll( links );

	} else if ( szPanelId.startsWith(Rid.PANEL_MAP) ) {
		RidElementPage page = getTopPage(szPanelId);
		if ( page == null ) {
			return gobs;
		}

		// Gobを作成する前に全てのエレメントのMainGobを無効にする
		// 20020430 Map画面にリンク線を張ることになったので、必要になった
		DocElement top = getTopZone();
		top.preCreateGobs( szPanelId ) ;

		// アクセスログを描画するにあたり、描画順序に制約が出てきたので、次の順序で描画する
		// ①線（選択可・不可の線、Circleの円を含む）
		// ②階層の深い（下位レベル階層の）ページ
		Vector gobVec = new Vector();
		gobVec.add( new Vector() ) ; // 線Gob格納領域
		// サークルマップの場合はルートが１つ
		if ( szPanelId.endsWith("Circle") ) {
			page.preCreateMapGobs(szPanelId);
			page.createMapGobs(szPanelId,1,gobVec);
		} else {
			if ( page.getMapParent(szPanelId,"Auto").equals("root") ) {
				// トップがルートの場合はページ一覧を描画する
				Vector pages = getTopZone().getPages(true);
				Vector tops = new Vector();
				Enumeration elm = pages.elements();
				while ( elm.hasMoreElements() ) {
					page = (RidElementPage)elm.nextElement();
					if ( page.getMapParent(szPanelId,"Auto").equals("root") ) {
						page.preCreateMapGobs(szPanelId);
						tops.add(page);
					}
				}
				if ( szPanelId.endsWith("Site") ) {
					// アクセスカウント最大値の更新
					updateAccessCount(szPanelId,tops);
				}
				elm = tops.elements();
				while ( elm.hasMoreElements() ) {
					page = (RidElementPage)elm.nextElement();
					page.createMapGobs(szPanelId,1,gobVec);
				}
			// トップがルートでなければ部分表示
			} else {
				page.preCreateMapGobs(szPanelId);
				if ( szPanelId.endsWith("Site") ) {
					// アクセスカウント最大値の更新
					Vector tops = new Vector();
					tops.add( page );
					updateAccessCount(szPanelId,tops);
				}
				page.createMapGobs(szPanelId,1,gobVec);
			}
		}

		// 集めてきた線をgobsに追加
		Vector lineGobs = (Vector)gobVec.elementAt(0);
		gobVec.removeElementAt(0);
		gobs.addAll( lineGobs );
		// 集めてきた階層ごとのページを逆順にgobsに追加
		Enumeration gobElm = new ZReverseEnumeration( gobVec );
		while ( gobElm.hasMoreElements() ) {
			gobs.addAll( (Vector)gobElm.nextElement() );
		}

		// コメントの描画
		page = getTopPage(szPanelId);
		DocElement comments = (DocElement)page.findChildElement(".Comments",null);
		if ( comments != null ) {
			for ( int nIndex=0; nIndex<comments.getLength() ; nIndex++ ) {
				Node elm = (Node)comments.item(nIndex);
				if ( elm instanceof DocElement ) {
					gobs.addAll(((DocElement)elm).createGobs( szPanelId,false ));
				}
			}
		}

		// シナリオエレメントの取得（先頭に追加する）
		Vector scenes = createScenarioGobs( szPanelId );
		scenes.addAll( gobs );
		gobs = scenes;

		// リンクエレメントの取得
		Vector links = createLinkGobs( szPanelId );
		gobs.addAll( links );
	}

	return gobs;
	}

	/**
	 * 描画エレメントの取得
	 * @param szPanelId パネル識別子
	 */
	public Vector createSkeltonGobs(String szPanelId) {
	Vector gobs = new Vector(); ;
	if ( szPanelId.equals(Rid.PANEL_ZONE) ) {
		gobs.addAll(super.createSkeltonGobs(szPanelId));

	} else if ( szPanelId.startsWith(Rid.PANEL_MAP) ) {
		RidElementPage page = getTopPage(szPanelId);
		// サークルマップの場合はルートが１つ
		if ( szPanelId.endsWith("Circle") ) {
			if ( page != null ) {
				gobs.addAll(page.createMapSkeltonGobs(szPanelId));
			}
		} else {
			if ( page != null ) {
				if ( page.getMapParent(szPanelId,"Auto").equals("root") ) {
					// ページ一覧を取得する
					Vector pages = getTopZone().getPages(true);
					Enumeration elm = pages.elements();
					while ( elm.hasMoreElements() ) {
						page = (RidElementPage)elm.nextElement();
						if ( page.getMapParent(szPanelId,"Auto").equals("root") ) {
							gobs.addAll(page.createMapSkeltonGobs(szPanelId));
						}
					}
				// トップがルートでなければ部分表示
				} else {
					gobs.addAll(page.createMapSkeltonGobs(szPanelId));
				}
			}
		}
	}

	return gobs;
	}

	/**
	 * リンクエレメントの取得
	 * @param szPanelId パネル識別子
	 */
	public Vector createLinkGobs(String szPanelId) {
	DocElement link = (DocElement)((DocElement)getDocumentElement()).findChildElement(".Link",null);
	Vector gobs = new Vector();
	if ( link != null ) {
		// 子ノードの描画
		for ( int nIndex=0; nIndex<link.getLength() ; nIndex++ ) {
			Node elm = (Node)link.item(nIndex);
			if ( elm instanceof RidElementLink ) {
				gobs.addAll(((RidElementLink)elm).createGobs( szPanelId ));
			}
		}
	}
	return gobs;
	}

	/**
	 * シナリオエレメントの取得
	 * @param szPanelId パネル識別子
	 */
	public Vector createScenarioGobs(String szPanelId) {
	RidElementScenario scene = getScenario(getScenarioName());
	Vector gobs = new Vector();
	if ( scene != null ) {
		// 子ノードの描画
		for ( int nIndex=0; nIndex<scene.getLength() ; nIndex++ ) {
			Node elm = (Node)scene.item(nIndex);
			if ( elm instanceof RidElementScenarioItem ) {
				gobs.addAll(((RidElementScenarioItem)elm).createGobs( szPanelId ));
			}
		}
	}
	return gobs;
	}

	//## アクセスカウント #############################################
	/**
	 * アクセスカウント最大値の更新
	 * CreateGob直前にcallされる
	 *
	 * @param szPanelId パネル識別子
	 * @param pages トップページ群
	 */
	protected void updateAccessCount( String szPanelId, Vector pages ) {
	MapModel model = getMapModel(szPanelId);

//	アクセスログを表示しない場合も、後でアクセスログ表示するかもしれないので、
//	アクセスカウントの最大数を検索する
	AccessCounts acc = null;
	if ( mUpdateAccessCount ) {
		// アクセスログ描画ルールの取得
		DocElement top = getTopZone();
		String szLogList = top.getAttribute("AccessLogList");
		String szLogType = top.getAttribute("AccessLogType");
		boolean[] bLogList = null;
		if ( szLogList.length() > 0 ) {
			bLogList = new boolean[szLogList.length()] ;
			for ( int nAt=0 ; nAt<szLogList.length() ; nAt++ ) {
				bLogList[nAt] = ( szLogList.charAt(nAt) != 'n' ) ;
			}
		}

		Enumeration elm = pages.elements();
		while ( elm.hasMoreElements() ) {
			RidElementPage page = (RidElementPage)elm.nextElement();
			AccessCounts acc2 = page.getMaxAccessCount(szPanelId,bLogList);
			if ( acc == null ) {
				acc = acc2 ;
			} else {
				acc.margeMax( acc2 );
			}
		}
		if ( acc != null ) {
			acc.setLogType( szLogType );
			acc.calcMaxCount();
		}
		model.setAccessCounts( acc );

	}
	mUpdateAccessCount = false;
	}

	/**
	 * アクセスカウント最大値再計算
	 */
	public void requestUpdateAccessCount() {
	mUpdateAccessCount = true;
	}

	//## 検索 #########################################################
	/**
	 * ゾーン内ユニットの検索
	 * @param szTag タグ名
	 * @param szKey キー
	 */
	protected RidElement findChildElementInZone( String szTag, String szKey ) {
	RidElement parent = getTopZone();
	String[] szKeys = ZString.tokenize(szKey,"/");

	// 子ゾーンをたどる
	for ( int nAt=0; nAt<(szKeys.length-1) ; nAt++ ) {
		parent = (RidElement)parent.findChildElement( ".ZoneUnit", "Name="+szKeys[nAt] );
		if ( parent == null ) {
			return null;
		}
	}

	// 最後に検索対象ユニットを取得
	return (RidElement)parent.findChildElement( "."+szTag, "File="+szKey );
	}

	//## Link #########################################################
	/**
	 * リンク先のページを設定しているリンク群を求める
	 * @param page         リンク先ページ
	 * @param szPanelId    Map画面における親ページか否かチェックする場合のパネル識別子
	 *                     (再計算の時のみfalse)
	 * @ret リンク群
	 */
	public Vector getToLink( RidElementPage page, String szPanelId ) {
	Vector    linkTo     = new Vector();
	Hashtable linkToHash = new Hashtable();

	DocElement linklist = (DocElement)((DocElement)getDocumentElement()).findChildElement(".Link",null);
	if ( linklist != null ) {
		NodeList list = linklist.getChildNodes();
		for ( int nAt=0 ; nAt<list.getLength() ; nAt++ ) {
			Node child = list.item(nAt);
			if ( child instanceof RidElementLink ) {
				RidElementLink linkUnit = (RidElementLink)child;
				RidElement toPage = linkUnit.getLinkToElement(page);
				if ( toPage != null && ( toPage instanceof RidElementPage ) ) {
					// それぞれのリンクユニットについて、重複がないように追加する
					if ( szPanelId != null ) {
						if ( !page.getAttribute("File").equals(((RidElementPage)toPage).getMapParent(szPanelId,null)) ) {
							continue;
						}
					}

					String szToFile = toPage.getAttribute("File");
					RidElementLink linkPrev = (RidElementLink)linkToHash.get( szToFile );
					if ( linkPrev == null ) {
						// 追加
						linkTo.add( linkUnit );
						linkToHash.put( szToFile, linkUnit );
					} else if ( !linkPrev.getAttribute("Intensity").equals("Virtual") &&
								 linkUnit.getAttribute("Intensity").equals("Virtual") ) {
						// 入れ替え
						linkTo.remove( linkPrev );
						linkTo.add( linkUnit );
						linkToHash.put( szToFile, linkUnit );
					} else {
						// 何もしない
					}
				}
			}
		}
	}
	return linkTo;
	}

	/**
	 * リンク元のページを求める
	 * @param page リンク先ページ
	 * @ret リンク元のページ群
	 */
	public Vector getFromPage( RidElementPage page ) {
	Vector pageFrom = new Vector();
	Hashtable pageFromHash = new Hashtable();

	DocElement linklist = (DocElement)((DocElement)getDocumentElement()).findChildElement(".Link",null);
	if ( linklist != null ) {
		NodeList list = linklist.getChildNodes();
		for ( int nAt=0 ; nAt<list.getLength() ; nAt++ ) {
			Node child = list.item(nAt);
			if ( child instanceof RidElementLink ) {
				RidElementLink linkUnit = (RidElementLink)child;
				RidElement fromPage = linkUnit.getLinkFromElement(page);
				if ( fromPage != null && ( fromPage instanceof RidElementPage ) ) {
					String szFromFile = fromPage.getAttribute("File");
					RidElementPage pagePrev = (RidElementPage)pageFromHash.get( szFromFile );
					if ( pagePrev == null ) {
						// 追加
						pageFrom.add( fromPage );
						pageFromHash.put( szFromFile, fromPage );
					}
				}
			}
		}
	}
	return pageFrom;
	}

	/**
	 * リンクエレメントをすべて未選択に設定
	 */
	public void setLinksUnSelect() {
	DocElement link = (DocElement)((DocElement)getDocumentElement()).findChildElement(".Link",null);
	Vector gobs = new Vector();
	if ( link != null ) {
		// 子ノードの描画
		for ( int nIndex=0; nIndex<link.getLength() ; nIndex++ ) {
			Node elm = (Node)link.item(nIndex);
			if ( elm instanceof RidElementLink ) {
				RidElementLink linkUnit = (RidElementLink)elm;
				linkUnit.setSelect(null,false);
			}
		}
	}
	}

	//## ＭＡＰ #######################################################
	/**
	 * 再計算リクエスト
	 */
	public void requestRecalcMap() {
	// 全てのマップを再計算する
	// Site
	DocElement root = (DocElement)getRootElement( Rid.PANEL_MAP+".Site" );
	DocElement map  = (DocElement)root.getChildElement(".ViewInfo.Map.Site",null);
	map.setAttribute("Calc","none");
	// Hier
	root = (DocElement)getRootElement( Rid.PANEL_MAP+".Hier" );
	map  = (DocElement)root.getChildElement(".ViewInfo.Map.Hier",null);
	map.setAttribute("Calc","none");
	// Circle
	root = (DocElement)getRootElement( Rid.PANEL_MAP+".Circle" );
	map  = (DocElement)root.getChildElement(".ViewInfo.Map.Circle",null);
	map.setAttribute("Calc","none");
	}

	/**
	 * 再計算(再計算対象ページの選定)
	 * @param szPanelId パネル識別子
	 * @param isZoneBase ゾーン優先アルゴリズム
	 */
	public void requestUpdateAccessCount(String szPanelId,boolean isZoneBase) {
	}
	
	/**
	 * 再計算(再計算対象ページの選定)
	 * @param szPanelId パネル識別子
	 * @param isZoneBase ゾーン優先アルゴリズム
	 */
	public ZRectangle recalcMap(String szPanelId,boolean isZoneBase) {
	// アクセスカウントの更新
	requestUpdateAccessCount();

	// トップページ(木の根となるページ)の設定
	RidElementPage topPage = null ;
	if ( szPanelId.endsWith("Circle") ) {
		topPage = getTopPage( szPanelId );
	} else {
		topPage = getDefaultTopPage( szPanelId );
	}
	if ( topPage == null ) {
		// ページが１枚もないので、再計算不要
		return null;
	}
//	ZDebug.trace("Recalc:"+topPage.getAttribute("Key"));

	// MapModelの初期化
	MapModel model = getMapModel(szPanelId);
	model.clearOffset();

	// ページ一覧をpagesに取得し、Map番号のクリアし、Topページをpagesの先頭に持ってくる
	RidElementZone top = getTopZone();
	Vector pageAll = top.getPages(true);
	Vector pages = new Vector();
	Enumeration elmPages = pageAll.elements();
	while ( elmPages.hasMoreElements() ) {
		RidElementPage page = (RidElementPage)elmPages.nextElement();
		page.clearMap(szPanelId);
		if ( page != topPage ) {
			pages.add( page );
		}
	}
	pages.add( 0, topPage );

	// 再計算STEP1:仮Major番号の付与と、親決め
	Vector topPages = null;
	if ( szPanelId.endsWith("Circle") ) {
		// CircleMapはルートが1つしか存在できない
		topPages = new Vector();
		topPages.add( topPage );
		recalcMapMajor( szPanelId, isZoneBase, topPages );
	} else {
		topPages = recalcMapMajor( szPanelId, isZoneBase, pages );
	}

	// 再計算STEP2:以前のMinor順序に並べ変え、Minor番号の設定
	MapMinorTree tree = recalcMapMinor( szPanelId, topPages, 1, false );

	// 深さを覚えておく
	DocElement elm = (DocElement)topPage.getChildElement(".ViewInfo."+szPanelId,null);
	elm.setIntAttr("Depth",tree.getDepth());

	// 再計算のコミット
	ZRectangle rtAll = null;
	elmPages = pages.elements() ;
	while ( elmPages.hasMoreElements() ) {
		RidElementPage page = (RidElementPage)elmPages.nextElement();
		ZRectangle rtPage = page.recalcMapEnd(szPanelId,tree.getMaxMinor());
		if ( rtAll == null ) {
			rtAll = rtPage;
		} else {
			rtAll.union( rtPage );
		}
	}
	
	if ( rtAll != null ) {
		// 描画矩形がわかったので、パネルサイズにあわせるように補正
		// rtAll にinsetをつける
		double INSET = 20;
		ZRectangle rtInset = new ZRectangle( rtAll.getX()-INSET,
											 rtAll.getY()-INSET,
											 rtAll.getWidth()+INSET*2,
											 rtAll.getHeight()+INSET*2 );
		rtAll = model.fitToPanel( rtInset, getLogicalSize() );
	}

	return rtAll;
	}

	/**
	 * STEP1:Major番号の設定と親決め
	 * @param szPanelId  パネル識別子
	 * @param isZoneBase ゾーン優先アルゴリズム
	 * @param pages      Topページになる可能性のあるページ
	 */
	public Vector recalcMapMajor(String szPanelId,boolean isZoneBase,Vector pages) {
	Vector topPages = new Vector();
	Enumeration elmPages = pages.elements();
	int nTopLevelId = 1;
	while ( elmPages.hasMoreElements() ) {
		RidElementPage page = (RidElementPage)elmPages.nextElement();

		boolean isCalced = page.recalcMapMajor( szPanelId, null, isZoneBase, nTopLevelId );
		// まだ計算されていなければレベル１のページなので、rootPagesに追加
		if ( isCalced ) {
			topPages.add( page );
		}
		nTopLevelId++;
	}
	return topPages;
	}

	/**
	 * STEP2:Minor番号を付与し、兄弟間の隙間があれば埋めて、MapTreeを返す
	 *
	 * @param szPanelId パネル識別子
	 * @param brothers  兄弟ページ
	 * @param minor     開始minor 番号
	 * @param bIndent   字下げ有無
	 */
	public MapMinorTree recalcMapMinor(String szPanelId,Vector brothers,double minor, boolean bIndent) {
	// 兄弟をソート
	Collections.sort(brothers,new PreMinorSorter());
	MapMinorTree tree = null;

	// それぞれの兄弟ページにおいて
	Enumeration elm = brothers.elements();
	while ( elm.hasMoreElements() ) {
		RidElementPage page = (RidElementPage)elm.nextElement();
		
		// 子供のページのMapTreeを計算
		double childMinor = (tree==null)?minor:tree.getMaxMinor()+1;
		MapMinorTree tree2 = page.recalcMapMinor(szPanelId, childMinor, bIndent);
		if ( tree == null ) {
//			ZDebug.trace("first tree:"+page.getAttribute("File"));
			tree = tree2;
		} else {
			if ( !szPanelId.endsWith("Circle") ) {
				// ２人目以降の子供の場合は、隙間をつめて、MapTreeを再計算
				double distance = tree.distance( tree2 );
//				ZDebug.trace("next tree:"+page.getAttribute("File")+",distance:"+distance);
				if ( distance > 1 ) {
					tree2 = page.recalcMapMinorMove( szPanelId, 1 - distance );
				} else if ( distance <= 0 ) {
					// 隙間が少ない場合は間をあける
					tree2 = page.recalcMapMinorMove( szPanelId, 1 - distance );
				}
			}
			tree.marge( tree2 );
		}
		
		// とりあえず兄弟間のインデントだけつける
		// 見栄えが悪いので、多分インデントはやめるのでは？
		bIndent = (!bIndent);
	}
	return tree;
	}

	//## ソート用サブクラス ###########################################
	public class PreMinorSorter implements Comparator {
	/**
	 * 比較
	 */
	public int compare(Object o1, Object o2) {
	RidElementPage p1 = (RidElementPage)o1;
	RidElementPage p2 = (RidElementPage)o2;
	int nRet = 0;
	double diff = p1.wMinorPre-p2.wMinorPre;
	if ( diff > 0 ) {
		nRet = 1;
	} else if ( diff < 0 ) {
		nRet = -1;
	}
	return nRet;
	}

	/**
	 * ソータと等しいか？
	 */
	public boolean equals(Object obj) {
	return (obj instanceof PreMinorSorter );
	}
	
	}

	//## アトリビュート################################################
	/**
	 * 描画ルートエレメント
	 * ストレージに格納しないデータ
	 */
	protected Hashtable mRoot = new Hashtable();

	/**
	 * Mapモデル
	 */
	protected Hashtable mMapModel = new Hashtable();

	/**
	 * アクセスログ最大値に更新が必要フラグ
	 */
	protected boolean mUpdateAccessCount = true;
}
