
package jp.riken.brain.ni.samuraigraph.figure.java2d;

import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.util.ArrayList;

import javax.swing.JPopupMenu;
import javax.swing.SwingUtilities;

import jp.riken.brain.ni.samuraigraph.base.SGAxis;
import jp.riken.brain.ni.samuraigraph.base.SGData;
import jp.riken.brain.ni.samuraigraph.base.SGDrawingElement;
import jp.riken.brain.ni.samuraigraph.base.SGIAxisBreakElement;
import jp.riken.brain.ni.samuraigraph.base.SGIAxisElement;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElement;
import jp.riken.brain.ni.samuraigraph.base.SGIGraphElement;
import jp.riken.brain.ni.samuraigraph.base.SGILegendElement;
import jp.riken.brain.ni.samuraigraph.base.SGIPropertySettingListener;
import jp.riken.brain.ni.samuraigraph.base.SGISelectable;
import jp.riken.brain.ni.samuraigraph.base.SGISignificantDifferenceElement;
import jp.riken.brain.ni.samuraigraph.base.SGIStringElement;
import jp.riken.brain.ni.samuraigraph.base.SGITimingLineElement;
import jp.riken.brain.ni.samuraigraph.base.SGIUndoable;
import jp.riken.brain.ni.samuraigraph.base.SGProperties;
import jp.riken.brain.ni.samuraigraph.base.SGPropertyDialog;
import jp.riken.brain.ni.samuraigraph.base.SGTuple2d;
import jp.riken.brain.ni.samuraigraph.base.SGTuple2f;
import jp.riken.brain.ni.samuraigraph.data.SGSXYData;
import jp.riken.brain.ni.samuraigraph.figure.SGFigureElement;

import org.w3c.dom.Document;
import org.w3c.dom.Element;



/**
 * グラフの基本クラス
 */

public abstract class SGGraphElement extends SGFigureElement
	implements SGIGraphElement
{

	/**
	 *
	 */
	protected SGIAxisElement mAxisElement = null;


	/**
	 * 
	 */
	protected ArrayList mGroupSetList = new ArrayList();


	/**
	 * 
	 */
	protected static SGPropertyDialog mPropertyDialogForData = null;


	/**
	 * 
	 */
	public static final int MAX_NUMBER_OF_ANCHORS = 8;


	/**
	 * 
	 */
/*	public boolean clearFocusedObjects()
	{
		if( super.clearFocusedObjects() == false )
		{
			return false;
		}

		// notify change
		this.focusedDataChanged();

		return true;
	}
*/

	/**
	 * 
	 * @return
	 */
	public ArrayList getVisibleDataList()
	{
		ArrayList list = new ArrayList();
		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)this.mGroupSetList.get(ii);
			if( groupSet.isVisible() )
			{
				list.add( this.mDataList.get(ii) );
			}
		}
		
		return list;
	}



	/**
	 * コンストラクタ
	 */
	public SGGraphElement()
	{
		super();
	}



	/**
	 * 
	 * @return
	 */
	public String toString()
	{
		return new String("SGGraphElement");
	}



	/**
	 * 
	 * @return
	 */
	public boolean setDialogOwner( final Frame frame )
	{
		super.setDialogOwner(frame);
		this.createDataDialog();
		return true;
	}



	/**
	 * プロパティダイアログの作成
	 */
	protected abstract boolean createDataDialog();




	/**
	 * データの追加
	 * @param data 追加するデータ
	 * @return true:成功、false:失敗
	 */
/*	public boolean addData( final SGData data )
	{
		// set the name of data
		final String name = "data"+(this.mGroupSetList.size()+1);

		this.addData( data, name );
		
		return true;
	}
*/



	/**
	 * 全描画要素の位置を決定
	 */
	protected abstract boolean setAllDrawingElementsLocation();



	/**
	 *
	 */
	public boolean setAxisElement( final SGIAxisElement element )
	{
		mAxisElement = element;
		return true;
	}


	/**
	 * Set the name of a data object.
	 * @param data - the data object to get the name
	 */
	public String getDataName( final SGData data )
	{
		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		if( groupSet != null )
		{
			return groupSet.getName();
		}
		return null;
	}



	/**
	 * Set the name of a data object.
	 * @param data - the data object to set the name
	 * @return true:succeeded, false:failed
	 */
	public boolean setDataName( String name, SGData data )
	{
		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		if( groupSet != null )
		{
			groupSet.setName( name );
			return true;
		}
		return false;
	}



	/**
	 * 
	 */
	public SGProperties getDataProperties( SGData data )
	{
		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		if( groupSet != null )
		{
			return groupSet.getWholeProperties();
		}
		return null;
	}




	/**
	 * 
	 */
	public ArrayList getDrawingElementList( final SGData data )
	{
		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		if( groupSet != null )
		{
			return groupSet.getDrawingElementList();
		}
		return null;
	}



	/**
	 * 
	 */
	public ArrayList getVisibleFlagList( final SGData data )
	{
		if( data==null )
		{
			throw new IllegalArgumentException("data==null");
		}

		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		if( groupSet==null )
		{
			throw new Error();
		}
		ArrayList list = groupSet.getVisibleFlagList();		

		return list;
	}


	/**
	 * 
	 * @param data
	 * @return
	 */
	public boolean getVisibleInLegendFlag( SGData data )
	{
		if( data==null )
		{
			throw new IllegalArgumentException("data==null");
		}

		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		if( groupSet==null )
		{
			throw new Error();
		}

		boolean flag = groupSet.isVisibleInLegend();
		return flag;
	}



	/**
	 * 
	 */
	public SGAxis getXAxis( final SGData data )
	{
		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		
		if( groupSet != null )
		{
			return groupSet.mXAxis;
		}

		return null;
	}


	/**
	 * 
	 */
	public SGAxis getYAxis( final SGData data )
	{
		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		
		if( groupSet != null )
		{
			return groupSet.mYAxis;
		}

		return null;
	}


	/**
	 * 
	 */
	public SGAxis getZAxis( final SGData data )
	{
		final ElementGroupSetInGraph groupSet = this.getElementGroupSet(data);
		
		if( groupSet != null )
		{
			return groupSet.mZAxis;
		}

		return null;
	}



	/**
	 * ズーム
	 */
	public boolean zoom( final float ratio )
	{
		super.zoom(ratio);

		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)this.mGroupSetList.get(ii);
			groupSet.zoom(ratio);
		}

		this.setAllDrawingElementsLocation();

		return true;

	}




	/**
	 * 
	 */
	public boolean removeData( final SGData data )
	{
		SGElementGroupSet groupSet = this.getElementGroupSet(data);
		this.mDataList.remove(data);
		this.mGroupSetList.remove(groupSet);

		return true;
	}


	/**
	 * 
	 */
	public boolean setDataVisible( final SGData data, final boolean flag )
	{
		SGElementGroupSet groupSet = this.getElementGroupSet(data);
		groupSet.setVisible(flag);
		return true;
	}



	/**
	 * 
	 */
	public boolean setGraphRect(
		final float x, final float y, final float width, final float height )
	{
		super.setGraphRect(x,y,width,height);
		if( this.setAllDrawingElementsLocation() == false )
		{
			return false;
		}
		return true;
	}



	/**
	 * 同期を取る
	 */
	public boolean synchronize( final SGIFigureElement element )
	{

		boolean flag = true;
		if( element instanceof SGILegendElement )
		{
			final SGILegendElement lElement = (SGILegendElement)element;
			flag = this.synchronizeToLegendElement( lElement );
		}
		else if( element instanceof SGIAxisElement )
		{
//System.out.println("SGIAxisElement");

			final SGIAxisElement aElement = (SGIAxisElement)element;
			flag = this.synchronizeToAxisElement( aElement );
		}
		else if( element instanceof SGIStringElement )
		{
			
		}
		else if( element instanceof SGIGraphElement )
		{
			
		}
		else if( element instanceof SGIAxisBreakElement )
		{
			
		}
		else if( element instanceof SGISignificantDifferenceElement )
		{
			
		}
		else if( element instanceof SGITimingLineElement )
		{
			
		}

		else
		{
			flag = this.synchronizeArgument( element );
		}


		return flag;

	}




	/**
	 * 
	 */
	protected boolean synchronizeToAxisElement( final SGIAxisElement aElement )
	{
		return this.setAllDrawingElementsLocation();
	}



	/**
	 * 
	 */
	protected boolean synchronizeToLegendElement( final SGILegendElement lElement )
	{

		// レジェンドからデータのリストを取得し、自分の持っているデータリストと
		// 描画要素リストをそれに合わせて並び替える
		final ArrayList dataList = lElement.getDataList();
		if( dataList.size() != this.mDataList.size() )
		{
			throw new Error("dataList.size() != this.mDataList.size()");
		}
		final ArrayList dataListNew = new ArrayList();
		final ArrayList groupSetListNew = new ArrayList();
		for( int ii=0; ii<dataList.size(); ii++ )
		{
			final SGData data = (SGData)dataList.get(ii);
			for( int jj=this.mDataList.size()-1; jj>=0; jj-- )
			{
				final SGData data_ = (SGData)this.mDataList.get(jj);
				if( data.equals(data_) )
				{
					final SGData dataRemoved = (SGData)this.mDataList.remove(jj);
					dataListNew.add(dataRemoved);
					final SGElementGroupSet groupSetRemoved = (SGElementGroupSet)this.mGroupSetList.remove(jj);
					groupSetListNew.add(groupSetRemoved);
					break;
				}
			}
		}
		this.mDataList = dataListNew;
		this.mGroupSetList = groupSetListNew;			


		return true;
	}



	/**
	 * Synchronize the element given by the argument.
	 * @param element An object to be synchronized.
	 */
	public boolean synchronizeArgument( final SGIFigureElement element )
	{
	    // this shouldn't happen
	    throw new Error();
	}



	/**
	 * 
	 */
	protected boolean isInsideAxisRange(
		final SGTuple2d value,
		final SGAxis axisX,
		final SGAxis axisY )
	{
		return ( axisX.insideRange(value.x) && axisY.insideRange(value.y) );
	}




	/**
	 * 
	 */
	protected boolean calcLocationOfSXYData(
//		final SGTuple2d[] valueArray,
		final SGSXYData dataSXY,
		final SGAxis axisX,
		final SGAxis axisY,
		final SGTuple2f[] pointArray )
	{
//System.out.println("<< SGGraphElement::calcLocationOfSXYData >>");

		final int num = dataSXY.getPointsNumber();

		final SGTuple2d rangeX = axisX.getRange();
		final SGTuple2d rangeY = axisY.getRange();
		final double minX = rangeX.x;
		final double maxX = rangeX.y;
		final double minY = rangeY.x;
		final double maxY = rangeY.y;
		final int typeX = axisX.getScaleType();
		final int typeY = axisY.getScaleType();


		// グラフ内での値に変換
		double minXInScale = 0.0;
		double maxXInScale = 0.0;
		double minYInScale = 0.0;
		double maxYInScale = 0.0;
		final double[] xInScale = new double[num];
		final double[] yInScale = new double[num];

		if( typeX == SGAxis.LINEAR_TYPE )
		{
			minXInScale = minX;
			maxXInScale = maxX;
			for( int ii=0; ii<num; ii++)
			{
				xInScale[ii] = dataSXY.getXValue(ii).doubleValue();
			}
		}
		else if( typeX == SGAxis.LOG_TYPE )
		{
			minXInScale = Math.log(minX);
			maxXInScale = Math.log(maxX);
			for( int ii=0; ii<num; ii++)
			{
				xInScale[ii] = Math.log( dataSXY.getXValue(ii).doubleValue() );
			}
		}

		if( typeY == SGAxis.LINEAR_TYPE )
		{
			minYInScale = minY;
			maxYInScale = maxY;
			for( int ii=0; ii<num; ii++)
			{
				yInScale[ii] = dataSXY.getYValue(ii).doubleValue();
			}
		}
		else if( typeY == SGAxis.LOG_TYPE )
		{
			minYInScale = Math.log(minY);
			maxYInScale = Math.log(maxY);
			for( int ii=0; ii<num; ii++)
			{
				yInScale[ii] = Math.log( dataSXY.getYValue(ii).doubleValue() );
			}
		}


		// グラフ領域内でのデータ点の比率を計算
		// グラフ全体での位置を計算
		for( int ii=0; ii<num; ii++ )
		{
			// ratio in the graph area rectangle
			final float xRatio
				= (float)( ( xInScale[ii] - minXInScale )/( maxXInScale - minXInScale ) );
			final float yRatio
				= (float)( 1.0 - ( yInScale[ii] - minYInScale )/( maxYInScale - minYInScale ) );

			// set the location of points
			final float posX = ( this.mGraphRectX + xRatio*this.mGraphRectWidth );
			final float posY = ( this.mGraphRectY + yRatio*this.mGraphRectHeight );
			pointArray[ii].setValues( posX, posY );
		}

		return true;
	}



	/**
	 * 
	 */
	protected boolean calcLocationOfVXYData(
		final SGTuple2d[] startArray,
		final SGTuple2d[] endArray,
		final SGAxis axisX,
		final SGAxis axisY,
		final SGTuple2f[] startLocationArray,
		final SGTuple2f[] endLocationArray )
	{

		if( startArray==null || endArray==null || startLocationArray==null || endLocationArray==null )
		{
			throw new IllegalArgumentException("startArray==null || endArray==null || startLocationArray==null || endLocationArray==null");
		}

		if( startArray.length != endArray.length )
		{
			throw new IllegalArgumentException("startArray.length != endArray.length");
		}

		if( startLocationArray.length != endLocationArray.length )
		{
			throw new IllegalArgumentException("startLocationArray.length != endLocationArray.length");
		}

		final int num = startArray.length;

		final SGTuple2d rangeX = axisX.getRange();
		final SGTuple2d rangeY = axisY.getRange();
		final double minX = rangeX.x;
		final double maxX = rangeX.y;
		final double minY = rangeY.x;
		final double maxY = rangeY.y;
		final int typeX = axisX.getScaleType();
		final int typeY = axisY.getScaleType();



		// グラフ内での値に変換
		double minXInScale = 0.0;
		double maxXInScale = 0.0;
		double minYInScale = 0.0;
		double maxYInScale = 0.0;
		final double[] startXInScale = new double[num];
		final double[] startYInScale = new double[num];
		final double[] endXInScale = new double[num];
		final double[] endYInScale = new double[num];

		if( typeX == SGAxis.LINEAR_TYPE )
		{
			minXInScale = minX;
			maxXInScale = maxX;
			for( int ii=0; ii<num; ii++ )
			{
				startXInScale[ii] = startArray[ii].x;
				endXInScale[ii] = endArray[ii].x;
			}
		}
		else if( typeX == SGAxis.LOG_TYPE )
		{
			minXInScale = Math.log(minX);
			maxXInScale = Math.log(maxX);
			for( int ii=0; ii<num; ii++ )
			{
				startXInScale[ii] = Math.log(startArray[ii].x);
				endXInScale[ii] = Math.log(endArray[ii].x);
			}
		}

		if( typeY == SGAxis.LINEAR_TYPE )
		{
			minYInScale = minY;
			maxYInScale = maxY;
			for( int ii=0; ii<num; ii++ )
			{
				startYInScale[ii] = startArray[ii].y;
				endYInScale[ii] = endArray[ii].y;
			}
		}
		else if( typeY == SGAxis.LOG_TYPE )
		{
			minYInScale = Math.log(minY);
			maxYInScale = Math.log(maxY);
			for( int ii=0; ii<num; ii++ )
			{
				startYInScale[ii] = Math.log(startArray[ii].y);
				endYInScale[ii] = Math.log(endArray[ii].y);
			}
		}


		// グラフ領域内でのデータ点の比率を計算
		final float[] startXRatioArray = new float[num];
		final float[] startYRatioArray = new float[num];
		for( int ii=0; ii<num; ii++ )
		{
			startXRatioArray[ii] = (float)( (startXInScale[ii]-minXInScale)/(maxXInScale-minXInScale) );
			startYRatioArray[ii] = (float)( 1.0 - (startYInScale[ii]-minYInScale)/(maxYInScale-minYInScale) );
		}

		final float[] endXRatioArray = new float[num];
		final float[] endYRatioArray = new float[num];
		for( int ii=0; ii<num; ii++ )
		{
			endXRatioArray[ii] = (float)( (endXInScale[ii]-minXInScale)/(maxXInScale-minXInScale) );
			endYRatioArray[ii] = (float)( 1.0 - (endYInScale[ii]-minYInScale)/(maxYInScale-minYInScale) );
		}


		// グラフ全体に対する比率を計算
		final float gx = this.mGraphRectX;
		final float gy = this.mGraphRectY;
		final float gw = this.mGraphRectWidth;
		final float gh = this.mGraphRectHeight;
		for( int ii=0; ii<num; ii++ )
		{
			startLocationArray[ii].setValues(
				gx + startXRatioArray[ii]*gw,
				gy + startYRatioArray[ii]*gh
			);
		}

		for( int ii=0; ii<num; ii++ )
		{
			endLocationArray[ii].setValues(
				gx + endXRatioArray[ii]*gw,
				gy + endYRatioArray[ii]*gh
			);
		}


		return true;
	}



	/**
	 * 
	 */
	public int getSelectedDataNumber()
	{
		return this.getFocusedObjectsList().size();
	}



	/**
	 * 
	 */
	public boolean getFocusedObjectsList( ArrayList list )
	{
		ArrayList gsList = this.getVisibleElementGroupSetList();
		for( int ii=0; ii<gsList.size(); ii++ )
		{
			SGISelectable s = (SGISelectable)gsList.get(ii);
			if( s.isSelected() )
			{
				list.add(s);
			}
		}
		return true;
	}



	/**
	 * Returns a list of copies of the focused data objects.
	 * @return a list to which the cut data objects are added
	 */
	public ArrayList getFocusedDataList()
	{
		ArrayList fList = this.getFocusedObjectsList();
		ArrayList list = new ArrayList();
		for( int ii=0; ii<fList.size(); ii++ )
		{
			ElementGroupSetInGraph gs = (ElementGroupSetInGraph)fList.get(ii);
			SGData data = this.getData(gs);
			list.add(data);
		}

		return list;
	}



	/**
	 * Cut focused copiable objects.
	 * @return a list of cut objects
	 */
	public ArrayList cutFocusedObjects()
	{
		return new ArrayList();
	}



	/**
	 * Returns a list of the cut data objects.
	 * @return a list to which the cut data objects are added
	 */
	public ArrayList cutFocusedData()
	{
		ArrayList list = this.getFocusedDataList();
		this.hideSelectedData();
		return list;
	}



	/**
	 * 
	 */
	public boolean hideSelectedObject( SGISelectable s )
	{
		ElementGroupSetInGraph el = (ElementGroupSetInGraph)s;
		el.setVisible(false);
		return true;
	}




	/**
	 * 
	 * @param e
	 * @param groupSet
	 * @return
	 */
	protected boolean clickElementGroupSet(
		final MouseEvent e,
		final ElementGroupSetInGraph groupSet )
	{

		final int x = e.getX();
		final int y = e.getY();
		final int cnt = e.getClickCount();

		// clicked on the line elements
		if( groupSet.contains(x,y) )
		{
			//
			this.updateFocusedObjectsList( groupSet, e );

			if( SwingUtilities.isLeftMouseButton(e) & cnt==1 )
			{

			}
			else if( SwingUtilities.isLeftMouseButton(e) & cnt==2 )
			{
				// show the property dialog
				this.setPropertyOfSelectedData();
			}
			else if( SwingUtilities.isRightMouseButton(e) & cnt==1 )
			{
				groupSet.mPopupMenu.show( this, x, y );
			}

			// notify change
//			this.focusedDataChanged();

			return true;
		}


/*
		if( e.getClickCount() == 1 )
		{

			// CtrlキーもShiftキーも押さない状態での
			// クリック１回でオブジェクトの選択/解除を行う。
			if(
				( (e.getModifiers()&MouseEvent.CTRL_MASK) == 0)
				&& ( (e.getModifiers()&MouseEvent.SHIFT_MASK) == 0)
			)
			{

				if( this.mFocusedObjectsList.contains(groupSet) )
				{
					// nothing to do
				}
				else
				{
					this.clearSelectedElementsGroupSet();
					this.addSelectedGroupSet(groupSet);
				}

			}
			else
			// CtrlキーまたはShiftキーを押した状態でのクリック１回で
			// 複数のオブジェクトの選択/解除を行う。
			{

				if( this.mFocusedObjectsList.contains(groupSet) )
				{
					this.removeSelectedGroupSet(groupSet);
				}
				else
				{
					// keep the present list
					ArrayList list = new ArrayList( this.mFocusedObjectsList );

					// add a new group set
					list.add( groupSet );

					// clear
					this.clearSelectedElementsGroupSet();

					// update the list
					for( int ii=0; ii<list.size(); ii++ )
					{
						ElementGroupSetInGraph gs
							= (ElementGroupSetInGraph)list.get(ii);
						this.addSelectedGroupSet(gs);
					}

				}

			}



			// 右ボタンクリック１回でポップアップメニューを出す。
			// オブジェクトが選択されている場合には、
			// 共通の項目を記述したポップアップメニューを出す。
			if( SwingUtilities.isRightMouseButton(e) )
			{
				groupSet.mPopupMenu.show( this, e.getX(), e.getY() );
			}


			// notify change
			this.focusedDataChanged();


			return true;
				
		}
		else if( e.getClickCount() == 2 )
		{

			// left button
			if( SwingUtilities.isLeftMouseButton(e) )
			{
				// show the property dialog
				this.setPropertyOfSelectedData();

				// notify change
				this.focusedDataChanged();

				return true;
			}

		}
*/

		return false;

	}



	/**
	 * 
	 * @param e
	 * @param groupSet
	 * @return
	 */
	protected boolean pressElementGroupSet(
		final MouseEvent e,
		final ElementGroupSetInGraph groupSet )
	{

		final boolean flag = groupSet.contains( e.getX(), e.getY() );
		if( !flag )
		{
			return false;
		}

		return true;
	}



	/**
	 * 
	 * @param e
	 * @param groupSet
	 * @return
	 */
	protected boolean dragElementGroupSet(
		final MouseEvent e,
		final ElementGroupSetInGraph groupSet )
	{

		if( this.getFocusedObjectsList().size()!=0 )
		{
			return true;
		}

		return false;
	}



	/**
	 * 
	 * @param groupSet
	 * @return
	 */
	protected boolean hideGroupSet(  final SGElementGroupSet groupSet )
	{
		// set invisible
		groupSet.setVisible(false);

		return true;
	}



	/**
	 * 
	 */
	protected boolean moveGroupSetToFront( final SGElementGroupSet groupSet )
	{
		SGData data = null;
		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			if( groupSet.equals(this.mGroupSetList.get(ii)) )
			{
				mGroupSetList.remove(ii);
				data = (SGData)mDataList.remove(ii);
				break;
			}
		}

		if( data == null )
		{
			return false;
		}

		mGroupSetList.add(mGroupSetList.size(),groupSet);
		mDataList.add(mDataList.size(),data);

		return true;
	}


	/**
	 * 
	 */
	protected boolean moveGroupSetToBack( final SGElementGroupSet groupSet )
	{
		SGData data = null;
		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			if( groupSet.equals(this.mGroupSetList.get(ii)) )
			{
				mGroupSetList.remove(ii);
				data = (SGData)mDataList.remove(ii);
				break;
			}
		}

		if( data == null )
		{
			return false;
		}

		mGroupSetList.add(0,groupSet);
		mDataList.add(0,data);

		return true;
	}


	/**
	 * 
	 */
	protected boolean removeGroupSet( final SGElementGroupSet groupSet )
	{
		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			if( groupSet.equals(this.mGroupSetList.get(ii)) )
			{
				mGroupSetList.remove(ii);
				mDataList.remove(ii);
				return true;
			}
		}

		return false;
	}



	/**
	 * 
	 */
	protected boolean hideSelectedData()
	{

		ArrayList list = this.getFocusedObjectsList();

		if( list.size() == 0 )
		{
			return true;
		}

		for( int ii=0; ii<list.size(); ii++ )
		{
			final SGElementGroupSet groupSet = (SGElementGroupSet)list.get(ii);
			groupSet.setVisible(false);
		}

		this.clearFocusedObjects();

		notifyChange();

		this.setChanged(true);

		return true;
	}



	/**
	 * 
	 */
	public boolean isDataVisible( SGData data )
	{
		SGElementGroupSet groupSet = this.getElementGroupSet(data);
		return groupSet.isVisible();
	}


	/**
	 * 
	 */
	public boolean removeSelectedData()
	{

		ArrayList list = this.getFocusedObjectsList();
		if( list.size() == 0 )
		{
			return true;
		}

		for( int ii=0; ii<list.size(); ii++ )
		{
			final SGElementGroupSet groupSet
				= (SGElementGroupSet)list.get(ii);
			this.removeGroupSet( groupSet );
		}

		notifyChange();

		this.setChanged(true);

		return true;
	}


	/**
	 * Move the focused objects to front or back.
	 * @param toFront - flag whether to front or back
	 * @return true:succeeded, false:failed
	 */
	public boolean moveFocusedObjects( boolean toFront )
	{
		if( toFront )
		{
			return this.moveSelectedDataToFront();
		}
		else
		{
			return this.moveSelectedDataToBack();
		}
	}



	/**
	 * 
	 */
	public boolean moveSelectedDataToFront()
	{
		ArrayList list = this.getFocusedObjectsList();

		if( list.size() == 0 )
		{
			return true;
		}

		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final SGElementGroupSet groupSet = (SGElementGroupSet)list.get(ii);
			if( this.moveGroupSetToFront( groupSet ) == false )
			{
				return false;
			}
		}

		notifyChange();

		this.setChanged(true);

		return true;
	}


	/**
	 * 
	 */
	public boolean moveSelectedDataToBack()
	{
		ArrayList list = this.getFocusedObjectsList();

		if( list.size() == 0 )
		{
			return true;
		}

		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final SGElementGroupSet groupSet
				= (SGElementGroupSet)list.get(ii);
			if( this.moveGroupSetToBack( groupSet ) == false )
			{
				return false;
			}
		}
		
		notifyChange();

		this.setChanged(true);

		return true;
	}



	/**
	 * 
	 * @return
	 */
/*	protected boolean clearSelectedGroupSet()
	{

		for( int ii=0; ii<this.mFocusedObjectsList.size(); ii++ )
		{
			final SGElementGroupSet groupSet = (SGElementGroupSet)this.mFocusedObjectsList.get(ii);
			groupSet.setSelected( false );
		}
		this.mFocusedObjectsList.clear();

		return true;
	}
*/


	/**
	 * 
	 */
	protected ElementGroupSetInGraph getElementGroupSet( SGData data )
	{
		for( int ii=0; ii<this.mDataList.size(); ii++ )
		{
			SGData data_ = (SGData)this.mDataList.get(ii);
			if( data_.equals(data) )
			{
				ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)this.mGroupSetList.get(ii);
				return groupSet;
			}
		}

		return null;
	}



	/**
	 * 
	 */
	protected SGData getData( ElementGroupSetInGraph groupSet )
	{
		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			SGElementGroupSet groupSet_ = (SGElementGroupSet)this.mGroupSetList.get(ii);
			if( groupSet_.equals(groupSet) )
			{
				SGData data = (SGData)this.mDataList.get(ii);
				return data;
			}
		}
		return null;
	}



	/**
	 * 
	 * @return
	 */
	public ArrayList getPropertySettingListerListOfDataElements()
	{
		return this.getFocusedObjectsList();
	}



	/**
	 * 
	 * @return
	 */
	public SGPropertyDialog getPropertyDialogForDataElements()
	{
		return mPropertyDialogForData;
	}



	/**
	 * 
	 * @return
	 */
	protected boolean drawAnchorsForFocusedObjects(
		final Point2D pos, final Graphics2D g2d )
	{
		SGUtilityForFigureElement.drawAnchorAsFocusedObject(pos,g2d);
		return true;
	}



	/**
	 * 
	 */
	public boolean onMouseClicked( final MouseEvent e )
	{

		if( super.onMouseClicked(e) == false )
		{
			return false;
		}

		// click the data
		if( this.clickDrawingElements(e) )
		{
			return true;
		}


		// if no data are clicked, clear the list of clicked data.
		this.clearFocusedObjects();

		// notify change
//		this.focusedDataChanged();


		return false;
	}



	/**
	 * 
	 * @param e
	 * @return
	 */
	public boolean clickDrawingElements( final MouseEvent e )
	{

		for( int ii=this.mGroupSetList.size()-1; ii>=0; ii-- )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)mGroupSetList.get(ii);
			if( groupSet.isVisible() ==false )
			{
				continue;
			}

			if( this.clickElementGroupSet( e, groupSet ) )
			{
				return true;
			}
		}

		return false;
	}



	/**
	 * 
	 * @param e
	 * @return
	 */
	public boolean pressDrawingElements( final MouseEvent e )
	{

		for( int ii=this.mGroupSetList.size()-1; ii>=0; ii-- )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)mGroupSetList.get(ii);

			if( this.pressElementGroupSet( e, groupSet ) )
			{
				return true;
			}
		}

		return false;
	}


	/**
	 * 
	 * @param e
	 * @return
	 */
	public boolean dragDrawingElements( final MouseEvent e )
	{

		for( int ii=this.mGroupSetList.size()-1; ii>=0; ii-- )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)mGroupSetList.get(ii);

			if( this.dragElementGroupSet( e, groupSet ) )
			{
				return true;
			}
		}

		return false;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onMousePressed( final MouseEvent e )
	{

		if( super.onMousePressed(e) == false )
		{
			return false;
		}

		final int x = e.getX();
		final int y = e.getY();


		// press the data
		if( this.pressDrawingElements(e) )
		{
			return true;
		}

		return false;

	}	

	

	/**
	 * 
	 * @param e
	 */
	public boolean onMouseDragged( final MouseEvent e )
	{

		if( super.onMouseDragged(e) == false )
		{
			return false;
		}

		// drag the data
		if( this.dragDrawingElements(e) )
		{
			return true;
		}

		return false;
	}


	/**
	 * 
	 * @param e
	 */
	public boolean onMouseReleased( final MouseEvent e )
	{

		if( super.onMouseReleased(e) == false )
		{
			return false;
		}

		return true;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onDrawingElement( final int x, final int y )
	{
		// グラフの領域外であれば終了
		if( !this.isInsideGraphArea(x,y) )
		{
			return false;
		}

/*
		// 描画要素に対してマウスが上にあるかどうか問い合わせる
		for( int ii=this.mGroupSetList.size()-1; ii>=0; ii-- )
		{
			final ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)mGroupSetList.get(ii);
			if( groupSet.onDrawingElement(x,y) )
			{
				this.mCursor = new Cursor( Cursor.HAND_CURSOR );
				return true;
			}
		}
*/

		return false;
	}


	/**
	 * 
	 * @return
	 */
	public boolean setTemporaryPropertiesOfFocusedObjects()
	{
		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean setChangedFocusedObjects()
	{
		return true;
	}


	/**
	 * 
	 */
	protected boolean drawRectangle(
		final double xMin, final double xMax, final double yMin, double yMax, final Graphics2D g2d )
	{

		if( g2d == null )
		{
			return false;
		}

		final Line2D lineUpper = new Line2D.Double( xMin, yMin, xMax, yMin );
		final Line2D lineLower = new Line2D.Double( xMin, yMax, xMax, yMax );
		final Line2D lineLeft = new Line2D.Double( xMin, yMin, xMin, yMax );
		final Line2D lineRight = new Line2D.Double( xMax, yMin, xMax, yMax );

		g2d.draw(lineUpper);
		g2d.draw(lineLower);
		g2d.draw(lineLeft);
		g2d.draw(lineRight);

		return true;
	}


	/**
	 * 
	 */
	public boolean changeXAxis( final SGData data )
	{

		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			final SGData data_ = (SGData)this.mDataList.get(ii);
			if( data_.equals(data) )
			{
				final ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)this.mGroupSetList.get(ii);
				this.changeXAxis( groupSet );
				return true;
			}

		}

		return false;
	}



	/**
	 * 
	 */
	public boolean changeYAxis( final SGData data )
	{

		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			final SGData data_ = (SGData)this.mDataList.get(ii);
			if( data_.equals(data) )
			{
				final ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)this.mGroupSetList.get(ii);
				this.changeYAxis( groupSet );
				return true;
			}

		}

		return false;
	}



	/**
	 * 
	 */
	public boolean changeZAxis( final SGData data )
	{
		return true;
	}



	/**
	 * 
	 */
	protected boolean changeXAxis( final ElementGroupSetInGraph groupSet )
	{
		SGAxis axisOfGroup = groupSet.mXAxis;

//System.out.println(axisOfGroup);

		final ArrayList list = mAxisElement.getXAxisList();

		SGAxis axisToChange = null;
		for( int ii=0; ii<list.size(); ii++ )
		{
			final SGAxis axis = (SGAxis)list.get(ii);
			if( axisOfGroup == axis )
			{
				axisToChange = (SGAxis)list.get((ii+1)%list.size());
				break;
			}
		}

		axisOfGroup = axisToChange;

//System.out.println(axisToChange);

		groupSet.mXAxis = axisToChange;

		return true;
	}



	/**
	 * 
	 */
	protected boolean changeYAxis( ElementGroupSetInGraph groupSet )
	{
		SGAxis axisOfGroup = groupSet.mYAxis;

//System.out.println(axisOfGroup);


		final ArrayList list = mAxisElement.getYAxisList();

		SGAxis axisToChange = null;
		for( int ii=0; ii<list.size(); ii++ )
		{
			final SGAxis axis = (SGAxis)list.get(ii);
			if( axisOfGroup == axis )
			{
				axisToChange = (SGAxis)list.get((ii+1)%list.size());
				break;
			}
		}

		axisOfGroup = axisToChange;

//System.out.println(axisToChange);

		groupSet.mYAxis = axisToChange;

		return true;
	}



	/**
	 * 
	 * @return
	 */
	public String getTagName()
	{
		return TAG_NAME_GRAPH;
	}

	
	/**
	 * 
	 */
	public Element createElement( final Document document )
	{
/*		Element el = this.createThisElement( document );
		if( el==null )
		{
			return null;
		}

		return el;
*/
		return null;
	}

	
	/**
	 * 
	 */
	public boolean writeProperty( final Element el )
	{
		return true;
	}

	
	
	/**
	 * 
	 */
	public boolean readProperty( final Element element )
	{
		return true;
	}
	
	
	/**
	 * 
	 * @param document
	 * @return
	 */
	public boolean createElementOfData( final Document document, final ArrayList list )
	{
		ArrayList groupSetList = this.getVisibleElementGroupSetList();
		for( int ii=0; ii<groupSetList.size(); ii++ )
		{
			ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)groupSetList.get(ii);

			Element el = groupSet.createElement( document );
			if( el==null )
			{
				return false;
			}
			el.setAttribute( KEY_DATA_TYPE, groupSet.getDataClassName() );

			list.add(el);
		}
		return true;
	}

	
	
	/**
	 * 
	 */
/*	public boolean writePropertyOfData( final Writer writer ) throws IOException
	{

		for( int ii=0; ii<this.mDataList.size(); ii++ )
		{
			writer.write( TAG_NAME_DATA + "\n" );

			final SGData data = (SGData)this.mDataList.get(ii);
			String className = null;

			if( data instanceof SGSXYData )
			{
				className = SGDataTypeConstants.SXY_DATA;
			}
			else if( data instanceof SGVXYData )
			{
				className = SGDataTypeConstants.VXY_DATA;
			}
			else if( data instanceof SGSXYMultipleData )
			{
				if( data instanceof SGSXYSamplingData )
				{
					className = SGDataTypeConstants.SXY_SAMPLING_DATA;
				}
				else
				{
					className = SGDataTypeConstants.SXY_MULTIPLE_DATA;
				}
			}
			else
			{
				return false;
			}

			SGUtilityText.writePropertyLine( writer, PF_CLASS_NAME, className );


			// データに対応したSGElementGroupSetオブジェクトを取得
			ElementGroupSetInGraph groupSet = this.getGraphData(data);
			groupSet.writePropertyOfData(writer);

		}


		writer.write("\n\n");

		return true;		

	}
*/


	/**
	 * 
	 */
	public SGProperties getProperties()
	{
		SGProperties p = new GraphProperties();
		if( this.getProperties(p) == false )
		{
			return null;
		}
		return p;
	}



	/**
	 * 
	 */
	public boolean getProperties( final SGProperties p )
	{
		if( ( p instanceof GraphProperties ) == false )
		{
			return false;
		}

		GraphProperties gp = (GraphProperties)p;
		gp.visibleElementGroupList = this.getVisibleElementGroupSetList();

		return true;
	}


	/**
	 * 
	 */
	public boolean setProperties( final SGProperties p )
	{
		if( ( p instanceof GraphProperties ) == false )
		{
			return false;
		}

		GraphProperties gp = (GraphProperties)p;


		boolean flag;
		flag = this.setVisibleElementGroupList( gp.visibleElementGroupList );
		if( !flag )
		{
			return false;
		}

		return true;

	}



	/**
	 * 
	 */
	protected ArrayList getVisibleElementGroupSetList()
	{
		ArrayList list = new ArrayList();

		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			SGElementGroupSet groupSet
				= (SGElementGroupSet)this.mGroupSetList.get(ii);
			if( groupSet.isVisible() )
			{
				list.add(groupSet);
			}
		}

		return list;
	}




	/**
	 * 
	 */
	protected boolean setVisibleElementGroupList( final ArrayList list )
	{

		ArrayList visibleList = new ArrayList( list );
		ArrayList invisibleList = new ArrayList();

		ArrayList visibleDataList = new ArrayList();
		ArrayList invisibleDataList = new ArrayList();

		for( int ii=0; ii<this.mGroupSetList.size(); ii++ )
		{
			ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)this.mGroupSetList.get(ii);
			final boolean b = list.contains(groupSet);
			groupSet.setVisible(b);
			if(!b)
			{
				invisibleList.add( groupSet );
				invisibleDataList.add( this.getData( groupSet ) );
			}
		}


		for( int ii=0; ii<visibleList.size(); ii++ )
		{
			ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)visibleList.get(ii);
			SGData data = this.getData(groupSet);
			visibleDataList.add(data);
		}


		this.mGroupSetList.clear();
		this.mDataList.clear();
		for( int ii=0; ii<visibleList.size(); ii++ )
		{
			this.mGroupSetList.add( visibleList.get(ii) );
			this.mDataList.add( visibleDataList.get(ii) );
		}
		for( int ii=0; ii<invisibleList.size(); ii++ )
		{
			this.mGroupSetList.add( invisibleList.get(ii) );
			this.mDataList.add( invisibleDataList.get(ii) );
		}


		return true;
	}



	/**
	 * 
	 * @return
	 */
	public boolean addPropertiesHistory( SGProperties p )
	{

		if( ( p instanceof GraphProperties ) == false )
		{
			return false;
		}

		boolean flag = super.addPropertiesHistory(p);		

		return flag;
	}



	/**
	 * 
	 */
	public boolean undo()
	{
		boolean flag = super.undo();
		if( !flag )
		{
			return false;
		}

		this.clearFocusedObjects();
		this.setAllDrawingElementsLocation();
		this.notifyChange();

		return true;
	}


	/**
	 * 
	 */
	public boolean redo()
	{
		boolean flag = super.redo();
		if( !flag )
		{
			return false;
		}

		this.clearFocusedObjects();
		this.setAllDrawingElementsLocation();
		this.notifyChange();

		return true;
	}



	/**
	 * 
	 */
	public boolean updateHistory()
	{
		return this.updateHistory_( this.getVisibleElementGroupSetList() );
	}


	/**
	 * 
	 */
	public boolean isChanged()
	{
		if( super.isChanged() )
		{
			return true;
		}
		ArrayList list = this.getVisibleElementGroupSetList();
		for( int ii=0; ii<list.size(); ii++ )
		{
			SGIUndoable el = (SGIUndoable)list.get(ii);
			if( el.isChanged() )
			{
				return true;
			}
		}
		return false;
	}





	/**
	 *
	 */
	protected abstract class ElementGroupSetInGraph
		extends SGElementGroupSetInFigureElement
		implements ActionListener, WindowListener,
			SGIUndoable, SGIPropertySettingListener, SGISelectable
	{


		/**
		 * Flag whether this object is focused.
		 */
		protected boolean mSelectedFlag = false;


		/**
		 * Get the flag as a focused object.
		 * @return whether this object is focused.
		 */
		public boolean isSelected()
		{
			return this.mSelectedFlag;
		}


		/**
		 * Set the flag as a focused object.
		 * @param b focused
		 */
		public void setSelected( final boolean b )
		{
			this.mSelectedFlag = b;
			for( int ii=0; ii<this.mDrawingElementGroupList.size(); ii++ )
			{
				IElementGroupInGraph group = (IElementGroupInGraph)this.mDrawingElementGroupList.get(ii);
				group.setFocused(b);
			}
		}


		/**
		 * 
		 */
		protected SGAxis mXAxis = null;


		/**
		 * 
		 */
		protected SGAxis mYAxis = null;


		/**
		 * 
		 */
		protected SGAxis mZAxis = null;



		/**
		 * 
		 */
		protected SGProperties mTemporaryProperties = null;


		/**
		 * ポップアップメニュー
		 */
		protected JPopupMenu mPopupMenu = new JPopupMenu();


		/**
		 * 
		 */
		protected ElementGroupSetInGraph()
		{
			super();
		}


		/**
		 * 
		 */
		protected boolean setXAxis( final SGAxis axis )
		{
			mXAxis = axis;
			return true;
		}


		/**
		 * 
		 */
		protected boolean setYAxis( final SGAxis axis )
		{
			mYAxis = axis;
			return true;
		}


		/**
		 * 
		 */
		protected boolean setZAxis( final SGAxis axis )
		{
			mZAxis = axis;
			return true;
		}



				
		/**
		 * 
		 */
		public SGAxis getXAxis()
		{
			return mXAxis;
		}


		/**
		 * 
		 */
		public SGAxis getYAxis()
		{
			return mYAxis;
		}

		
		/**
		 * 
		 */
		public SGAxis getZAxis()
		{
			return mZAxis;
		}



		/**
		 * 
		 */
		public ArrayList getVisibleFlagList()
		{

			final ArrayList list = new ArrayList();

			final ArrayList groupList = this.mDrawingElementGroupList;
			for( int jj=0; jj<groupList.size(); jj++ )
			{
				final SGElementGroup group = (SGElementGroup)groupList.get(jj);
				final boolean flag = group.isVisible();
				list.add( new Boolean(flag) );
			}

			return list;
		}



		/**
		 * 
		 */
		public void windowActivated(final WindowEvent e)
		{
//System.out.println("windowActivated");
		}


		/**
		 * 
		 */
		public void windowDeactivated(final WindowEvent e)
		{
//System.out.println("windowDeactivated");
		}


		/**
		 * 
		 */
		public void windowIconified(final WindowEvent e)
		{
//System.out.println("windowIconified");
		}


		/**
		 * 
		 */
		public void windowDeiconified(final WindowEvent e)
		{
//System.out.println("windowDeiconified");
		}


		/**
		 * 
		 */
		public void windowOpened(final WindowEvent e)
		{
//System.out.println("windowOpened");
		}


		/**
		 * 
		 */
		public void windowClosed(final WindowEvent e)
		{
//System.out.println("windowClosed");
		}


		/**
		 * 
		 */
		public void windowClosing(final WindowEvent e)
		{
//System.out.println("windowClosing");

			Object obj = e.getSource();
//System.out.println(obj);
			if( obj.equals( mPropertyDialogForData ) )
			{
				this.cancel();
			}

		}



		/**
		 * 
		 */
		public void actionPerformed( final ActionEvent e )
		{
			final Object source = e.getSource();
			final String command = e.getActionCommand();

			if( command.equals( SGGraphElement.MENUCMD_PROPERTY ) )
			{
				setPropertyOfSelectedData();
			}
			else if( command.equals( MENUCMD_MOVE_TO_FRONT )
				| command.equals( MENUCMD_MOVE_TO_BACK )
				| command.equals( MENUCMD_CUT )
				| command.equals( MENUCMD_COPY )
				| command.equals( MENUCMD_PASTE )
				| command.equals( MENUCMD_DELETE )
			)
			{
				notifyToListener( command );
			}

		}


		/**
		 * 
		 */
		protected abstract boolean createDrawingElements();


		/**
		 * 
		 */
		public boolean onDrawingElement( final int x, final int y )
		{
			for( int ii=0; ii<this.mDrawingElementGroupList.size(); ii++ )
			{
				SGElementGroup group = (SGElementGroup)this.mDrawingElementGroupList.get(ii);
				SGDrawingElement[] array = group.mDrawingElementArray;
				for( int jj=0; jj<array.length; jj++ )
				{
					if( array[jj].contains(x,y) )
					{
						return true;
					}
				}
			}
			return false;
		}



		/**
		 * 
		 */
		public boolean showPropertyDialog()
		{
			final SGPropertyDialog dg = mPropertyDialogForData;

			dg.addPropertySettingListener(this);
			dg.setLocation( mDialogOwner.getLocation() );

			return true;
		}


		protected boolean mChangedFlag = false;

		
		/**
		 * 
		 */
		public boolean commit()
		{

			final boolean flag = this.setPropertiesFromDialog();
			if( !flag )
			{
				return false;
			}

			SGProperties pTemp = this.mTemporaryProperties;
			SGProperties pPresent = this.getWholeProperties();

			if( pTemp.equals(pPresent) == false )
			{
				this.mChangedFlag = true;
			}

			this.mTemporaryProperties = null;
			mPropertyDialogForData.setVisible(false);

			//
			if( setAllDrawingElementsLocation() == false )
			{
				return false;
			}

			notifyChange();

			return true;
		}



		/**
		 * 
		 */
		public boolean cancel()
		{
			if( this.recover() == false )
			{
				throw new Error();
			}

			this.mTemporaryProperties = null;

			final SGPropertyDialog dg = mPropertyDialogForData;


			//
			if( setAllDrawingElementsLocation() == false )
			{
				return false;
			}
			notifyChange();

			return true;
		}


		/**
		 * 
		 */
		public boolean preview()
		{
			boolean flag = this.setPropertiesFromDialog();
			if( !flag )
			{
				return false;
			}

			//
			if( setAllDrawingElementsLocation() == false )
			{
				return false;
			}
			notifyChange();

			return true;
		}


		/**
		 * 
		 */
		public boolean setPropertiesFromDialog()
		{
			return true;
		}



		/**
		 * 
		 * @return
		 */
		protected boolean recover()
		{
			if( this.setWholeProperties( this.mTemporaryProperties ) == false )
			{
				return false;
			}

			return true;
		}



		/**
		 * 
		 */
		public SGProperties getProperties()
		{
			ElementGroupSetInGraphProperties ep = new ElementGroupSetInGraphProperties();
			if( this.getProperties(ep) == false )
			{
				return null;
			}

			return ep;
		}


		/**
		 * 
		 */
		public boolean getProperties( final SGProperties p )
		{
			if( ( p instanceof ElementGroupSetInGraphProperties ) == false ) return false;

			super.getProperties(p);

			ElementGroupSetInGraphProperties ep = (ElementGroupSetInGraphProperties)p;

			ep.xAxis = mAxisElement.getConfigurationInCube(this.mXAxis);
			ep.yAxis = mAxisElement.getConfigurationInCube(this.mYAxis);
//			ep.zAxis = mAxisElement.getConfigurationInCube(this.mZAxis);

			return true;
		}



		/**
		 * 
		 */
		public boolean setProperties( final SGProperties p )
		{

			if( ( p instanceof ElementGroupSetInGraphProperties ) == false ) return false;

			if( super.setProperties(p) == false ) return false;

			ElementGroupSetInGraphProperties ep = (ElementGroupSetInGraphProperties)p;

			this.mXAxis = mAxisElement.getAxisInCube( ep.xAxis );
			this.mYAxis = mAxisElement.getAxisInCube( ep.yAxis );
//			this.mZAxis = mAxisElement.getAxisInCube( ep.zAxis );

			return true;
		}


		
		/**
		 * 
		 * @return
		 */
		public abstract String getDataClassName();


		
		public Element createElement( final Document document )
		{
			Element el = super.createElement( document );
			el.setAttribute( KEY_DATA_TYPE, this.getDataClassName() );
			return el;
		}
		
		


		/**
		 * 
		 * @return
		 */
		public String getTagName()
		{
			return SGGraphElement.TAG_NAME_DATA;
		}

		

		/**
		 * 
		 * @return
		 */
		public boolean setDrawingElementProperties( final ArrayList dElementList )
		{

			final ArrayList groupList = this.mDrawingElementGroupList;
			if( dElementList.size() != groupList.size() )
			{
				throw new IllegalArgumentException("dElementList.size() != groupList.size()");
			}

			// set the properties of drawing elements
			for( int ii=0; ii<groupList.size(); ii++ )
			{
				SGElementGroup group = (SGElementGroup)groupList.get(ii);
				SGDrawingElement dElement = (SGDrawingElement)dElementList.get(ii);
				group.setProperty( dElement );
			}

			return true;
		}



		/**
		 * 
		 * @return
		 */
		private ArrayList mElementGroupSetPropertyHistoryList = new ArrayList();


		/**
		 * 
		 * @return
		 */
		private int mElementGroupSetStateCounter = 0;


		/**
		 * 
		 */
		public boolean initPropertiesHistory()
		{
			this.addElementGroupSetPropertyHistory( (WholeProperties)this.getWholeProperties() );
			return true;
		}


		/**
		 * 
		 * @return
		 */
		private boolean addElementGroupSetPropertyHistory( WholeProperties p )
		{

//	  System.out.println("<< addFigurePropertyHistory >>");
//	  System.out.println(this.mFigureStateCounter);

			ArrayList list = new ArrayList();
			for( int ii=0; ii<this.mElementGroupSetStateCounter; ii++ )
			{
				list.add( this.mElementGroupSetPropertyHistoryList.get(ii) );
			}
			list.add(p);

			this.mElementGroupSetPropertyHistoryList = list;

//	  System.out.println(this.mFigurePropertyHistoryList);
//	  System.out.println("x="+p.x+"  y="+p.y);
//	  System.out.println("w="+p.width+"  h="+p.height);
//	  System.out.println();

			return true;
		}


		/**
		 * 現在、状態が履歴のどの位置にあるのかを示すカウンタ
		 */
		private int mCurrentStateCounter = 0;


		/**
		 * アンドゥ対象オブジェクトの履歴リスト
		 */
		protected ArrayList mUndoableObjectHistoryList = new ArrayList();


		/**
		 * アンドゥを行う
		 */
		public boolean undo()
		{
//System.out.println("undo : ElemntGroupSet");
//System.out.println();

			this.mElementGroupSetStateCounter--;

			WholeProperties p
				= (WholeProperties)this.mElementGroupSetPropertyHistoryList.get(
					this.mElementGroupSetStateCounter);
//System.out.println(p);
//System.out.println();

			this.setWholeProperties(p);

			setAllDrawingElementsLocation();
//			notifyChange();
//			repaint();

			return true;
		}


		/**
		 * リドゥを行う
		 */
		public boolean redo()
		{
//System.out.println("redo : ElemntGroupSet");
//System.out.println();

			this.mElementGroupSetStateCounter++;

			WholeProperties p
				= (WholeProperties)this.mElementGroupSetPropertyHistoryList.get(
					this.mElementGroupSetStateCounter);
//System.out.println(p);
//System.out.println();

			this.setWholeProperties(p);

			setAllDrawingElementsLocation();
//			notifyChange();
//			repaint();

			return true;
		}



		/**
		 * アンドゥ操作を依頼する
		 */
		public boolean onUndo()
		{
			return this.undo();
		}

	
	
		/**
		 * リドゥ操作を依頼する
		 */
		public boolean onRedo()
		{
			return this.redo();
		}


		/**
		 * オブジェクトの履歴を更新する
		 * @return
		 */
		public boolean updateObjectHistory( final SGIUndoable obj )
		{
			ArrayList objList = new ArrayList();
			objList.add(obj);
			boolean flag = this.updateObjectHistory(objList);
			if( !flag )
			{
				return false;
			}

			return true;
		}



		/**
		 * オブジェクトの履歴を更新する
		 */
		public boolean updateObjectHistory( final ArrayList objList )
		{

			ArrayList list = new ArrayList();
			for( int ii=0; ii<this.mCurrentStateCounter; ii++ )
			{
				Object obj = this.mUndoableObjectHistoryList.get(ii);
				list.add(obj);
			}
			list.add( new ArrayList(objList) );

			this.mUndoableObjectHistoryList = list;
			this.mCurrentStateCounter++;

//System.out.println("<< updateObjectHistory >>");
//System.out.println(this.mUndoableObjectHistoryList);
//System.out.println(this.mCurrentStateCounter);
//System.out.println();

			return true;
		}



		/**
		 * 履歴の更新の実行。アンドゥ対象の操作が行われたときに、真っ先に呼ばれる。
		 */
		public boolean updateHistory()
		{
			if( this.mChangedFlag )
			{
				this.updateThisObjectHistory();
				this.setChanged(false);
				this.updateObjectHistory( this );
			}

			return true;
		}



		/**
		 * 
		 */
		public boolean updateThisObjectHistory()
		{

			// オブジェクト履歴を更新
//			this.updateObjectHistory(this);


			// フィギュアのプロパティ履歴を更新
			this.mElementGroupSetStateCounter++;
			this.addElementGroupSetPropertyHistory( (WholeProperties)this.getWholeProperties() );

			return true;
		}



		/**
		 * 
		 *
		 */
		public void notifyToRoot()
		{
			notifyToRootFromFigureElement();
		}


		/**
		 * 
		 * @return
		 */
		public boolean isChanged()
		{
			return this.mChangedFlag;
		}

		
		/**
		 * 
		 */
		public void setChanged( final boolean b )
		{
			this.mChangedFlag = b;
		}


		/**
		 * 
		 */
		class ElementGroupSetInGraphProperties	extends ElementGroupSetPropertiesInFigureElement
		{
//			SGAxis xAxis;
//			SGAxis yAxis;
//			SGAxis zAxis;

			int xAxis;
			int yAxis;
			int zAxis;


			/**
			 * 
			 */
			public boolean equals( final Object obj )
			{
				if( ( obj instanceof ElementGroupSetInGraphProperties ) == false )
				{
					return false;
				}

				if( super.equals(obj) == false ) return false;

				ElementGroupSetInGraphProperties p = (ElementGroupSetInGraphProperties)obj;

				if( p.xAxis!=this.xAxis ) return false;
				if( p.yAxis!=this.yAxis ) return false;

				return true;
			}


			/**
			 * 
			 */
			public String toString()
			{
				String str = new String("[");
				str += super.toString();
				str += new String("xAxis="+xAxis+", ");
				str += new String("yAxis="+yAxis+", ");
				str += new String("zAxis="+zAxis+", ");
				str += new String("]");

				return str;
			}

		}


	}




	/**
	 * 
	 */
	abstract class ElementGroupSetForMultipleData extends ElementGroupSetInGraph
		implements ActionListener
	{

		/**
		 * 
		 */
		protected ArrayList mElementGroupSetList = new ArrayList();


		/**
		 * 
		 */
		protected ElementGroupSetForMultipleData()
		{
			super();
		}


		/**
		 * 
		 */
		public ArrayList getVisibleFlagList()
		{
			if( this.mElementGroupSetList.size()==0 )
			{
				return new ArrayList();
			}
			ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)this.mElementGroupSetList.get(0);
			ArrayList list = groupSet.getVisibleFlagList();
			return list;
		}


		/**
		 * 
		 */
		public void setSelected( final boolean b )
		{
			this.mSelectedFlag = b;
			for( int ii=0; ii<this.mElementGroupSetList.size(); ii++ )
			{
				SGISelectable groupSet = (SGISelectable)this.mElementGroupSetList.get(ii);
				groupSet.setSelected(b);
			}
		}


		/**
		 * 
		 */
		public boolean contains( final int x, final int y )
		{
			for( int ii=0; ii<this.mElementGroupSetList.size(); ii++ )
			{
				SGElementGroupSet groupSet = (SGElementGroupSet)this.mElementGroupSetList.get(ii);
				if( groupSet.contains(x,y) )
				{
					return true;
				}
			}
			return false;
		}


		/**
		 * 
		 */
		public boolean setMagnification( final float mag )
		{
			super.setMagnification(mag);
			for( int ii=0; ii<this.mElementGroupSetList.size(); ii++ )
			{
				SGElementGroupSet groupSet
					= (SGElementGroupSet)this.mElementGroupSetList.get(ii);
				groupSet.setMagnification(mag);
			}

			return true;
		}


		/**
		 * 
		 */
		public boolean zoom( final float ratio )
		{
			super.zoom(ratio);

			for( int ii=0; ii<this.mElementGroupSetList.size(); ii++ )
			{
				SGElementGroupSet groupSet
					= (SGElementGroupSet)this.mElementGroupSetList.get(ii);
				groupSet.zoom(ratio);
			}

			return true;
		}

		/**
		 * 
		 */
		public boolean addDrawingElementGroup( final int type )
		{

			for( int ii=0; ii<this.mElementGroupSetList.size(); ii++ )
			{
				final ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)this.mElementGroupSetList.get(ii);
				groupSet.addDrawingElementGroup(type);
			}

			return true;
		}


		/**
		 * 
		 */
		public boolean addDrawingElementGroup( final SGDrawingElement element )
		{

			for( int ii=0; ii<this.mElementGroupSetList.size(); ii++ )
			{
				final ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)this.mElementGroupSetList.get(ii);
				groupSet.addDrawingElementGroup(element);
			}

			return true;
		}



		/**
		 * 
		 */
		public boolean onDrawingElement( final int x, final int y )
		{

			for( int ii=this.mElementGroupSetList.size()-1; ii>=0; ii-- )
			{
				final ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)this.mElementGroupSetList.get(ii);
				boolean flag = groupSet.onDrawingElement(x,y);
				if( flag )
				{
					return true;
				}
			}

			return false;
		}


		/**
		 * 
		 */
		public void paintGraphics2D( final Graphics2D g2d )
		{
			for( int ii=this.mElementGroupSetList.size()-1; ii>=0; ii-- )
			{
				final ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)this.mElementGroupSetList.get(ii);
				groupSet.paintGraphics2D(g2d);
			}
		}



		/**
		 * 
		 */
		public ArrayList getDrawingElementList()
		{
			final ArrayList list = new ArrayList();

			if( this.mElementGroupSetList.size() == 0 )
			{
				return list;
			}

			// 最初のグループからだけ取得する
			final SGElementGroupSet groupSet = (SGElementGroupSet)this.mElementGroupSetList.get(0);
			for( int ii=0; ii<groupSet.mDrawingElementGroupList.size(); ii++ )
			{
				final SGElementGroup group = (SGElementGroup)groupSet.mDrawingElementGroupList.get(ii);
				final SGDrawingElement dElement = group.getDrawingElement();
				list.add( dElement );
			}

			return list;
		}



		/**
		 * 
		 * @return
		 */
		public boolean setDrawingElementProperties( final ArrayList dElementList )
		{

			final ArrayList groupSetList = this.mElementGroupSetList;

			// set the properties of drawing elements
			for( int ii=0; ii<groupSetList.size(); ii++ )
			{
				ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)groupSetList.get(ii);
				groupSet.setDrawingElementProperties( dElementList );
			}

/*
			final ArrayList groupList = this.mDrawingElementGroupList;
			if( dElementList.size() != groupList.size() )
			{
				throw new IllegalArgumentException("dElementList.size() != groupList.size()");
			}

			// set the properties of drawing elements
			for( int ii=0; ii<groupList.size(); ii++ )
			{
				SGElementGroup group = (SGElementGroup)groupList.get(ii);
				SGDrawingElement dElement = (SGDrawingElement)dElementList.get(ii);
				group.setProperty( dElement );
			}
*/

			return true;
		}


	}


	/**
	 * 
	 */
	public static interface IElementGroupInGraph
	{
		public void setFocused( final boolean b );
		
		public boolean isFocused();
	}


	/**
	 * 
	 */
	public static class GraphProperties extends SGProperties
	{

		ArrayList visibleElementGroupList = new ArrayList();


		/**
		 * 
		 *
		 */
		public GraphProperties()
		{
			super();
		}


		/**
		 * 
		 */
		public boolean equals( final Object obj )
		{
			if( ( obj instanceof GraphProperties ) == false )
			{
				return false;
			}

			GraphProperties p = (GraphProperties)obj;

			if( p.visibleElementGroupList.equals(this.visibleElementGroupList) == false )
			{
				return false;
			}
			return true;
		}


	}



}

