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

import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.swing.JLabel;
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.SGIFigureElementAxisBreak;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementAxis;
import jp.riken.brain.ni.samuraigraph.base.SGIConstants;
import jp.riken.brain.ni.samuraigraph.base.SGIData;
import jp.riken.brain.ni.samuraigraph.base.SGIDrawingElementConstants;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElement;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementGraph;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementGrid;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementLegend;
import jp.riken.brain.ni.samuraigraph.base.SGINode;
import jp.riken.brain.ni.samuraigraph.base.SGIPropertyDialogObserver;
import jp.riken.brain.ni.samuraigraph.base.SGISelectable;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementShape;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementSignificantDifference;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementString;
import jp.riken.brain.ni.samuraigraph.base.SGIFigureElementTimingLine;
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.base.SGUtility;
import jp.riken.brain.ni.samuraigraph.base.SGUtilityText;
import jp.riken.brain.ni.samuraigraph.figure.SGElementGroup;
import jp.riken.brain.ni.samuraigraph.figure.SGElementGroupSet;
import jp.riken.brain.ni.samuraigraph.figure.SGFigureElement;
import jp.riken.brain.ni.samuraigraph.figure.SGIFigureDrawingElementConstants;
import org.w3c.dom.Document;
import org.w3c.dom.Element;



/**
 * An object drawing drawing elements of data.
 */
public abstract class SGFigureElementGraph extends SGFigureElement
	implements SGIFigureElementGraph, SGIFigureDrawingElementConstants,
		SGIDrawingElementConstants
{

	/**
	 *
	 */
	protected SGIFigureElementAxis mAxisElement = null;


	/**
	 * 
	 */
	protected SGPropertyDialog mDialog = null;


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


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



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


	public void dispose()
	{
		super.dispose();
		this.mDialog.dispose();
		this.mDialog = null;
	}


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



	/**
	 * 
	 * @return
	 */
	public String getInstanceDescription()
	{
		return "Graph";
	}



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



	/**
	 * Update the location of all drawing elements with data.
	 */
	protected boolean updateAllDrawingElementsLocation()
	{
		List gsList = this.mChildList;
		List dList = this.mDataList;
		for( int ii=0; ii<gsList.size(); ii++ )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)gsList.get(ii);

			if( groupSet.isVisible() == false )
			{
				continue;
			}

			final SGData data = (SGData)dList.get(ii);

			// calculate coordinates in graph
			if( groupSet.updateDrawingElementsLocation(data) == false )
			{
				return false;
			}
		}

		return true;
	}




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



	/**
	 * 
	 * @param data
	 * @param name
	 * @return
	 */
	public boolean addData( final SGData data, final String name )
	{
		if( super.addData(data,name) == false )
		{
			return false;
		}

		// create an ElementGroupSetInGraph object
		ElementGroupSetInGraph groupSet = this.createGroupSet(data,name);
		if( groupSet==null )
		{
			return false;
		}

		// add to list
		this.addToList( groupSet );


		// initialize the properties
		groupSet.initPropertiesHistory();


		// create property dialog
		if( this.mDialog==null )
		{
			this.createDataDialog();
		}


		// set the change flag and notify to the root
		this.setChanged(true);
		this.notifyToRoot();

		return true;
	}



	/**
	 * Add a data object with a set of properties.
	 * @param data - added data.
	 * @param name - the "right" name set to be data.
	 * @param p - properties set to be data.
	 * @return true:succeeded, false:failed
	 */
	public boolean addData( final SGData data, final String name, final SGProperties p )
	{

		if( super.addData(data,name) == false )
		{
			return false;
		}

		// create an ElementGroupSetInGraph object
		ElementGroupSetInGraph groupSet = this.createGroupSet(data,name);
		if( groupSet==null )
		{
			return false;
		}

		// add to list
		this.addToList( groupSet );

		// set properties
		if( groupSet.setWholeProperties(p) == false )
		{
			return false;
		}


		// set visible because cut data objects are set invisible
		groupSet.setVisible(true);


		// Set the given name to data.
		// The name given in p would be redundant.
		groupSet.setName(name);


		// initialize the properties
		groupSet.initPropertiesHistory();

		// create property dialog
		if( this.mDialog==null )
		{
			this.createDataDialog();
		}


		// set the change flag and notify to the root
		this.setChanged(true);
		this.notifyToRoot();

		return true;
	}


	/**
	 * Create an instance of ElementGroupSetInGraph.
	 * @param data - related data
	 * @param name - the name of data
	 * @return
	 */
	protected abstract ElementGroupSetInGraph createGroupSet( SGData data, String name );



	/**
	 * 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.getXAxis();
		}

		return null;
	}


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

		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.mChildList.size(); ii++ )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)this.mChildList.get(ii);
			groupSet.zoom(ratio);
		}

		this.updateAllDrawingElementsLocation();
		updateImage();

		return true;

	}



	/**
	 * Overrode to remove a object related to the data.
	 */
	public boolean removeData( final SGData data )
	{
		// remove group set
		SGElementGroupSet groupSet = this.getElementGroupSet(data);
		this.removeChild(groupSet);

		// remove data
		return super.removeData(data);
	}



	/**
	 * 
	 */
	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.updateAllDrawingElementsLocation() == false )
		{
			return false;
		}
		this.updateImage();
		return true;
	}



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

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

			final SGIFigureElementAxis aElement = (SGIFigureElementAxis)element;
			flag = this.synchronizeToAxisElement( aElement, msg );
		}
		else if( element instanceof SGIFigureElementString )
		{
			
		}
		else if( element instanceof SGIFigureElementGraph )
		{
			
		}
		else if( element instanceof SGIFigureElementAxisBreak )
		{
			
		}
		else if( element instanceof SGIFigureElementSignificantDifference )
		{
			
		}
		else if( element instanceof SGIFigureElementTimingLine )
		{
			
		}
		else if( element instanceof SGIFigureElementGrid )
		{

		}
		else if( element instanceof SGIFigureElementShape )
		{

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

		return flag;
	}




	/**
	 * 
	 */
	protected boolean synchronizeToAxisElement( final SGIFigureElementAxis aElement, final String msg )
	{
		this.updateAllDrawingElementsLocation();
		this.updateImage();
		return true;
	}



	/**
	 * 
	 */
	protected boolean synchronizeToLegendElement( final SGIFigureElementLegend lElement, final String msg )
	{

		// reorder the data list and child list with those of legend
		final List 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.mChildList.remove(jj);
					groupSetListNew.add(groupSetRemoved);
					break;
				}
			}
		}


		for( int ii=dataListNew.size()-1; ii>=0; ii-- )
		{
			SGData data = (SGData)dataListNew.get(ii);

			ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)groupSetListNew.get(ii);

			boolean diffFlag = false;

			SGProperties wp = lElement.getDataProperties(data);
			SGProperties wp_ = groupSet.getWholeProperties();

//System.out.println("graph:"+msg+"  "+wp.equals(wp_));

			if( !wp.equals(wp_) & !msg.equals( SGIFigureElement.NOTIFY_CHANGE_ON_UNDO ) )
//			if( !wp.equals(wp_) )
			{
				diffFlag = true;
			}

//			// name of data
//			String nameNew = lElement.getDataName(data);
//			String nameOld = groupSet.getName();
//			if( nameNew.equals( nameOld ) == false )
//			{
//				groupSet.setChanged(true);
//			}
//			groupSet.setName( nameNew );
//
//
//			// axes
//			SGAxis xAxis = lElement.getXAxis(data);
//			if( xAxis!=null & groupSet.getXAxis()!=null )
//			{
//				if( xAxis.equals(groupSet.getXAxis()) == false )
//				{
//					groupSet.setChanged(true);
//				}
//			}
//			groupSet.setXAxis(xAxis);
//
//			SGAxis yAxis = lElement.getYAxis(data);
//			if( yAxis!=null & groupSet.getYAxis()!=null )
//			{
//				if( yAxis.equals(groupSet.getYAxis()) == false )
//				{
//					groupSet.setChanged(true);
//				}
//			}
//			groupSet.setYAxis(yAxis);
//
//			SGAxis zAxis = lElement.getZAxis(data);
//			if( zAxis!=null & groupSet.mZAxis!=null )
//			{
//				if( zAxis.equals(groupSet.mZAxis) == false )
//				{
//					groupSet.setChanged(true);
//				}
//				groupSet.setZAxis(zAxis);
//			}
//
//
//			// visible
//			boolean visibleFlag = lElement.isDataVisible(data);
//			if( visibleFlag != groupSet.isVisible() )
//			{
//				this.setChanged(true);
//			}
//			groupSet.setVisible( visibleFlag );
//
//
//			// drawing element of data
//			ArrayList sList = lElement.getDrawingElementList(data);
//			final boolean b = groupSet.synchronizeDrawingElement(sList);
//			if(b)
//			{
//				diffFlag = true;
//			}

			if( diffFlag )
			{
				groupSet.setChanged(true);
			}

			if( groupSet.setWholeProperties(wp) == false )
			{
				return false;
			}

		}


		// set to the attribute
		this.mDataList = dataListNew;
		this.mChildList = groupSetListNew;			


		// update the drawing elements
		this.updateAllDrawingElementsLocation();
		updateImage();

		return true;
	}


	/**
	 * Synchronize the element given by the argument.
	 * @param element An object to be synchronized.
	 */
	public boolean synchronizeArgument( final SGIFigureElement element, final String msg )
	{
	    // 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 calcLocationOfPoints(
		final double[] xValueArray,
		final double[] yValueArray,
		final SGAxis axisX,
		final SGAxis axisY,
		final SGTuple2f[] pointArray )
	{

		if( xValueArray==null || yValueArray==null )
		{
			throw new IllegalArgumentException("xValueArray==null || yValueArray==null");
		}

		if( xValueArray.length!=yValueArray.length )
		{
			throw new IllegalArgumentException("xValueArray.length!=yValueArray.length");
		}

		final int num = xValueArray.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();

		final boolean invcoordX = axisX.isInvertCoordinates();
		final boolean invcoordY = axisY.isInvertCoordinates();

		// グラフ内での値に変換
		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_SCALE )
		{
			minXInScale = minX;
			maxXInScale = maxX;
		}
		else if( typeX == SGAxis.LOG_SCALE )
		{
			minXInScale = Math.log(minX);
			maxXInScale = Math.log(maxX);
		}

		if( typeY == SGAxis.LINEAR_SCALE )
		{
			minYInScale = minY;
			maxYInScale = maxY;
		}
		else if( typeY == SGAxis.LOG_SCALE )
		{
			minYInScale = Math.log(minY);
			maxYInScale = Math.log(maxY);
		}

		// グラフ領域内でのデータ点の比率を計算
		// グラフ全体での位置を計算
		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++ )
		{
			if( typeX == SGAxis.LINEAR_SCALE )
			{
				xInScale[ii] = xValueArray[ii];
			}
			else if( typeX == SGAxis.LOG_SCALE )
			{
				xInScale[ii] = Math.log( xValueArray[ii] );
			}
			if( typeY == SGAxis.LINEAR_SCALE )
			{
				yInScale[ii] = yValueArray[ii];
			}
			else if( typeY == SGAxis.LOG_SCALE )
			{
				yInScale[ii] = Math.log( yValueArray[ii] );
			}
			// ratio in the graph area rectangle
			float xRatio, yRatio;
			if ( invcoordX )
				xRatio = (float)( ( maxXInScale - xInScale[ii] )/( maxXInScale - minXInScale ) );
			else
				xRatio = (float)( ( xInScale[ii] - minXInScale )/( maxXInScale - minXInScale ) );
			if ( invcoordY )
				yRatio = (float)( 1.0 - ( maxYInScale - yInScale[ii] )/( maxYInScale - minYInScale ) );
			else 
				yRatio = (float)( 1.0 - ( yInScale[ii] - minYInScale )/( maxYInScale - minYInScale ) );

			// set the location of points
			final float posX = gx + xRatio*gw;
			final float posY = gy + yRatio*gh;
			pointArray[ii].setValues( posX, posY );
		}

		return true;
	}



	/**
	 * 
	 */
	protected boolean calcLocationOfPairs(
		final SGTuple2d[] startValueArray,
		final SGTuple2d[] endValueArray,
		final SGAxis axisX,
		final SGAxis axisY,
		final SGTuple2f[] startLocationArray,
		final SGTuple2f[] endLocationArray )
	{

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

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

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

		final int num = startValueArray.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();
		final boolean invcoordX = axisX.isInvertCoordinates();
		final boolean invcoordY = axisY.isInvertCoordinates();


		// グラフ内での値に変換
		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_SCALE )
		{
			minXInScale = minX;
			maxXInScale = maxX;
		}
		else if( typeX == SGAxis.LOG_SCALE )
		{
			minXInScale = Math.log(minX);
			maxXInScale = Math.log(maxX);
		}

		if( typeY == SGAxis.LINEAR_SCALE )
		{
			minYInScale = minY;
			maxYInScale = maxY;
		}
		else if( typeY == SGAxis.LOG_SCALE )
		{
			minYInScale = Math.log(minY);
			maxYInScale = Math.log(maxY);
		}


		final float[] startXRatioArray = new float[num];
		final float[] startYRatioArray = new float[num];
		final float[] endXRatioArray = new float[num];
		final float[] endYRatioArray = new float[num];
		final double diffXInScale = maxXInScale-minXInScale;
		final double diffYInScale = 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++ )
		{
			// start and end scale in the graph area rectangle
			if( typeX == SGAxis.LINEAR_SCALE )
			{
				startXInScale[ii] = startValueArray[ii].x;
				endXInScale[ii] = endValueArray[ii].x;
			}
			else if( typeX == SGAxis.LOG_SCALE )
			{
				startXInScale[ii] = Math.log(startValueArray[ii].x);
				endXInScale[ii] = Math.log(endValueArray[ii].x);
			}
			if( typeY == SGAxis.LINEAR_SCALE )
			{
				startYInScale[ii] = startValueArray[ii].y;
				endYInScale[ii] = endValueArray[ii].y;
			}
			else if( typeY == SGAxis.LOG_SCALE )
			{
				startYInScale[ii] = Math.log(startValueArray[ii].y);
				endYInScale[ii] = Math.log(endValueArray[ii].y);
			}
			
			// ratio in the graph area rectangle
			if ( invcoordX ) {
				startXRatioArray[ii] = (float)( 1.0 - (startXInScale[ii]-minXInScale)/(diffXInScale) );
				endXRatioArray[ii] = (float)( 1.0 - (endXInScale[ii]-minXInScale)/(diffXInScale) );
			}else {
				startXRatioArray[ii] = (float)( (startXInScale[ii]-minXInScale)/(diffXInScale) );
				endXRatioArray[ii] = (float)( (endXInScale[ii]-minXInScale)/(diffXInScale) );
			}
			if ( invcoordY ) {
				startYRatioArray[ii] = (float)( (startYInScale[ii]-minYInScale)/(diffYInScale) );
				endYRatioArray[ii] = (float)( (endYInScale[ii]-minYInScale)/(diffYInScale) );
			} else {
				startYRatioArray[ii] = (float)( 1.0 - (startYInScale[ii]-minYInScale)/(diffYInScale) );
				endYRatioArray[ii] = (float)( 1.0 - (endYInScale[ii]-minYInScale)/(diffYInScale) );
			}
			// set the location of points
			startLocationArray[ii].x = gx + startXRatioArray[ii]*gw;
			startLocationArray[ii].y = gy + startYRatioArray[ii]*gh;
			endLocationArray[ii].x = gx + endXRatioArray[ii]*gw;
			endLocationArray[ii].y = gy + endYRatioArray[ii]*gh;
		}

		return true;
	}



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



	/**
	 * 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;
	}



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

		return true;
	}



	/**
	 * 
	 */
	protected boolean removeGroupSet( final SGElementGroupSet groupSet )
	{
		for( int ii=0; ii<this.mChildList.size(); ii++ )
		{
			if( groupSet.equals(this.mChildList.get(ii)) )
			{
				this.mChildList.remove(ii);
				this.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;
	}



	/**
	 * Overrode to move data objects in the list.
	 * @param obj
	 * @param list
	 * @return
	 */
	protected boolean moveObjectToHead(
		final Object obj, final List list )
	{
		return this.moveObjectTo( obj, list, list.size()-1 );
	}

	/**
	 * Overrode to move data objects in the list.
	 * @param obj
	 * @param list
	 * @return
	 */
	protected boolean moveObjectToTail(
		final Object obj, final List list )
	{
		return this.moveObjectTo( obj, list, 0 );
	}

	private boolean moveObjectTo(
		final Object obj, final List list, final int indexNew )
	{
		if( ( obj instanceof ElementGroupSetInGraph) == false )
		{
			return false;
		}

		final int index = list.indexOf(obj);
		if( index==-1 )
		{
			return false;
		}

		List dList = this.mDataList;
		SGData data = (SGData)dList.get(index);

		if( SGUtility.moveObject( obj, list, indexNew ) == false )
		{
			return false;
		}

		if( SGUtility.moveObject( data, dList, indexNew ) == false )
		{
			return false;
		}

		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.mChildList.get(ii);
				return groupSet;
			}
		}

		return null;
	}



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



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



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



	/**
	 * 
	 * @param g2d
	 * @param symbol
	 * @return
	 */
	protected boolean drawAnchorsOnRectangle(
		final Rectangle2D rect,
		final Graphics2D g2d )
	{
		final float x = (float)rect.getX();
		final float y = (float)rect.getY();
		final float w = (float)rect.getWidth();
		final float h = (float)rect.getHeight();
		Point2D pos = new Point2D.Float();
		pos.setLocation( x, y );
		drawAnchorsForFocusedObjects( pos, g2d );
		pos.setLocation( x+w, y );
		drawAnchorsForFocusedObjects( pos, g2d );
		pos.setLocation( x, y+h );
		drawAnchorsForFocusedObjects( pos, g2d );
		pos.setLocation( x+w, y+h );
		drawAnchorsForFocusedObjects( pos, g2d );
		return true;
	}


	/**
	 * 
	 * @return
	 */
	protected JPopupMenu createGroupSetPopupMenu( ElementGroupSetInGraph groupSet )
	{
		JPopupMenu p = new JPopupMenu();
		p.setBounds( 0, 0, 100, 100 );

		p.add( new JLabel( "  -- Data --" ) );
		p.addSeparator();

		SGUtility.addItem( p, groupSet, MENUCMD_MOVE_TO_FRONT );
		SGUtility.addItem( p, groupSet, MENUCMD_MOVE_TO_BACK );

		p.addSeparator();

		SGUtility.addItem( p, groupSet, MENUCMD_CUT );
		SGUtility.addItem( p, groupSet, MENUCMD_COPY );
		SGUtility.addItem( p, groupSet, MENUCMD_PASTE );

		p.addSeparator();

		SGUtility.addItem( p, groupSet, MENUCMD_DELETE );

		p.addSeparator();

		SGUtility.addItem( p, groupSet, MENUCMD_PROPERTY );

		return p;
	}


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

		// click the data
		List list = this.mChildList;
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)list.get(ii);
			if( groupSet.isVisible() ==false )
			{
				continue;
			}

			if( groupSet.onMouseClicked(e) )
			{
				return true;
			}
		}


		return false;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onMousePressed( final MouseEvent e )
	{
		List list = this.mChildList;
		for( int ii=list.size()-1; ii>=0; ii-- )
		{
			final ElementGroupSetInGraph groupSet
				= (ElementGroupSetInGraph)list.get(ii);
			if( !groupSet.isVisible() )
			{
				continue;
			}
			if( groupSet.onMousePressed(e) )
			{
				return true;
			}
		}

		return false;
	}	

	

	/**
	 * 
	 * @param e
	 */
	public boolean onMouseDragged( final MouseEvent e )
	{
		if( this.getFocusedObjectsList().size()!=0 )
		{
			return true;
		}

		return true;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onMouseReleased( final MouseEvent e )
	{
		return true;
	}



	/**
	 * 
	 * @param e
	 */
	public boolean onDrawingElement( final int x, final int y )
	{
/*
		// 描画要素に対してマウスが上にあるかどうか問い合わせる
		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;
	}



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

	
	/**
	 * 
	 */
	public Element createElement( final Document document )
	{
		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.getVisibleChildList();
		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.getDataType() );

			list.add(el);
		}
		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.getVisibleChildList();

		return true;
	}


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

		GraphProperties gp = (GraphProperties)p;


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

		return true;

	}



	/**
	 * 
	 */
	protected boolean setVisibleChildList( final List list )
	{

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

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

		for( int ii=0; ii<this.mChildList.size(); ii++ )
		{
			ElementGroupSetInGraph groupSet = (ElementGroupSetInGraph)this.mChildList.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.mChildList.clear();
		this.mDataList.clear();
		for( int ii=0; ii<visibleList.size(); ii++ )
		{
			this.mChildList.add( visibleList.get(ii) );
			this.mDataList.add( visibleDataList.get(ii) );
		}
		for( int ii=0; ii<invisibleList.size(); ii++ )
		{
			this.mChildList.add( invisibleList.get(ii) );
			this.mDataList.add( invisibleDataList.get(ii) );
		}


		return true;
	}



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

		this.clearFocusedObjects();
		this.updateAllDrawingElementsLocation();
		updateImage();
		this.notifyChangeOnUndo();

		return true;
	}


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

		this.clearFocusedObjects();
		this.updateAllDrawingElementsLocation();
		updateImage();
		this.notifyChangeOnUndo();

		return true;
	}



	/**
	 * 
	 */
	protected Set getAvailableChildSet()
	{
		Set set = new HashSet();
		List mList = this.getMementoList();
		for( int ii=0; ii<mList.size(); ii++ )
		{
			GraphProperties p = (GraphProperties)mList.get(ii);
			set.addAll( p.visibleElementGroupList );
		}

		return set;
	}


	// remove useless child objects.
	protected boolean removeUselessChild()
	{
		Set set = this.getAvailableChildSet();
		if( set.size()!=0 )
		{
			boolean gc = false;
			List cList = new ArrayList( this.mChildList );
			for( int ii=cList.size()-1; ii>=0; ii-- )
			{
				Object obj = cList.get(ii);
				if( set.contains(obj) == false )
				{
					SGData d = this.getData( (ElementGroupSetInGraph)obj );
					this.removeChild(obj);
					this.mDataList.remove(d);
					gc = true;
				}
				obj = null;
			}

			if( gc )
			{
				cList.clear();
				set.clear();
//				System.gc();
				this.notifyToListener( SGIFigureElement.MERGE_DATA );
			}
		}

		return true;
	}



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



//	public void paint( final Graphics g, final boolean clip )
//	{
//		final int mode = this.getMode();
//		if( mode == MODE_LIGHT )
//		{
//			if( this.mImg!=null )
//			{
//				Graphics2D g2d = (Graphics2D)g;
//				g2d.drawImage( this.mImg, null, this.getComponent() );
//			}
//		}
//		else
//		{
//			super.paint(g,clip);
//		}
//	}



	//
	protected ElementGroupSetInGraph getGroupSet( final int id )
	{
		ElementGroupSetInGraph el = (ElementGroupSetInGraph)this.getChildObject(id);
		if( el == null ) return null;
		if( el.isVisible() == false ) return null;
		return el;
	}


	public boolean setAxisXDirectly( final int id, final int value )
	{
		ElementGroupSetInGraph el = this.getGroupSet(id);
		if( el==null ) return false;
		if( this.setDirectlyBefore(el) == false ) return false;
		if( el.setXAxisLocation( value ) == false ) return false;
		if( this.setDirectlyAfter(el) == false ) return false;
		return true;
	}

	public boolean setAxisYDirectly( final int id, final int value )
	{
		ElementGroupSetInGraph el = this.getGroupSet(id);
		if( el==null ) return false;
		if( this.setDirectlyBefore(el) == false ) return false;
		if( el.setYAxisLocation( value ) == false ) return false;
		if( this.setDirectlyAfter(el) == false ) return false;
		return true;
	}

	public boolean setVisibleInLegendDirectly( final int id , final boolean value )
	{
		ElementGroupSetInGraph el = this.getGroupSet(id);
		if( el==null ) return false;
		if( this.setDirectlyBefore(el) == false ) return false;
		if( el.setVisibleInLegend( value ) == false ) return false;
		if( this.setDirectlyAfter(el) == false ) return false;
		return true;
	}

	public boolean setNameDirectly( final int id , final String value )
	{
		ElementGroupSetInGraph el = this.getGroupSet(id);
		if( el==null ) return false;
		if( this.setDirectlyBefore(el) == false ) return false;
		if( el.setName( value ) == false ) return false;
		if( this.setDirectlyAfter(el) == false ) return false;
		return true;
	}


	protected boolean setDirectlyBefore( ElementGroupSetInGraph el )
	{
		return el.prepare();
	}

	protected boolean setDirectlyAfter( ElementGroupSetInGraph el )
	{
		if( el.commit() == false )
		{
			return false;
		}
		this.notifyChange();
		this.notifyToRoot();
		this.repaint();
		return true;
	}



	//
	protected boolean readProperty( final SGElementGroup group, final Element el )
	{
		String str;
		Boolean b;

		// visible
		str = el.getAttribute( KEY_VISIBLE );
		if( str.length()!=0 )
		{
			b = SGUtilityText.getBoolean(str);
			if( b==null )
			{
				return false;
			}
			group.setVisible( b.booleanValue() );
		}

		return true;
	}



	/**
	 * 
	 */
	protected int setDataProperties(
		final Element el, final ElementGroupSetInGraph gs, final SGData data )
	{

		final int ic = SGIConstants.PROPERTY_FILE_INCORRECT;
		SGIFigureElementAxis aElement = this.mAxisElement;
		String str = null;

		// name of data
		str = el.getAttribute( KEY_DATA_NAME );
		if( str.length()==0 )
		{
			return ic;
		}
		final String name = str;
		if( gs.setName(name) == false )
		{
			return ic;
		}

		// x-axis
		str = el.getAttribute( KEY_X_AXIS_POSITION );
		if( str.length()!=0 )
		{
			final SGAxis xAxis = aElement.getAxis( str );
			if( xAxis==null )
			{
				return ic;
			}
			gs.setXAxis( xAxis );
		}

		// y-axis
		str = el.getAttribute( KEY_Y_AXIS_POSITION );
		if( str.length()!=0 )
		{
			final SGAxis yAxis = aElement.getAxis( str );
			if( yAxis==null )
			{
				return ic;
			}
			gs.setYAxis( yAxis );
		}

		// visibility in legend
		str = el.getAttribute( KEY_VISIBLE_IN_LEGEND );
		if( str.length()!=0 )
		{
			Boolean b = SGUtilityText.getBoolean(str);
			if( b==null )
			{
				return ic;
			}
			final boolean vLegend = b.booleanValue();
			if( gs.setVisibleInLegend(vLegend) == false )
			{
				return ic;
			}
		}

		return SGIConstants.SUCCESSFUL_COMPLETION;
	}



	/**
	 * @author  okumura
	 */
	protected abstract class ElementGroupSetInGraph
		extends SGElementGroupSetInFigureElement
		implements ActionListener,
			SGIPropertyDialogObserver, SGISelectable,
			SGINode, ChildObject
	{

		// the ID number
		private int mID;

		public int getID()
		{
			return this.mID;
		}

		public boolean setID( final int id )
		{
			this.mID = id;
			return true;
		}


		/**
		 * 
		 */
		public boolean setXAxisLocation( final int config )
		{
			this.mXAxis = this.getAxis(config);
			return true;
		}

		/**
		 * 
		 */
		public boolean setYAxisLocation( final int config )
		{
			this.mYAxis = this.getAxis(config);
			return true;
		}

		private SGAxis getAxis( final int config )
		{
			return SGFigureElementGraph.this.mAxisElement.getAxisInPlane( config );
		}


		public void finalize()
		{
//			System.out.println("finalize:"+this);
		}


		/**
		 * 
		 */
		public void dispose()
		{
			super.dispose();
			this.mPopupMenu = null;
			this.mTemporaryProperties = null;
		}


		/**
		 * 
		 */
		protected boolean mClipFlag = true;


		/**
		 * 
		 * @param b
		 */
		protected void setClipFlag( final boolean b )
		{
			this.mClipFlag = b;
		}

		
		/**
		 * 
		 * @return
		 */
		protected boolean getClipFlag()
		{
			return this.mClipFlag;
		}


		/**
		 * 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;
			
			ArrayList list = new ArrayList( this.mDrawingElementGroupList );
			for( int ii=0; ii<list.size(); ii++ )
			{
				IElementGroupInGraph group = (IElementGroupInGraph)list.get(ii);
				group.setFocused(b);
			}
		}



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


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


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



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

			return list;
		}



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

			if( command.equals( SGIConstants.MENUCMD_PROPERTY ) )
			{
				setPropertiesOfSelectedObjects();
			}
			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();




		/**
		 * 
		 * @param e
		 * @param groupSet
		 * @return
		 */
		protected boolean onMouseClicked( final MouseEvent e )
		{

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

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

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

				return true;
			}

			return false;
		}



		/**
		 * 
		 * @param e
		 * @param groupSet
		 * @return
		 */
		protected boolean onMousePressed( final MouseEvent e )
		{
			return this.contains( e.getX(), e.getY() );
		}



		/**
		 * 
		 * @param dg
		 * @return
		 */
		public boolean commit()
		{

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

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

			this.mTemporaryProperties = null;

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

			updateImage();
			repaint();
			notifyChange();

			return true;
		}

		

		/**
		 * 
		 */
		public boolean cancel()
		{
			if( this.setWholeProperties( this.mTemporaryProperties ) == false )
			{
				return false;
			}

			this.mTemporaryProperties = null;

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

			updateImage();
			repaint();
			notifyChange();

			return true;
		}


		/**
		 * 
		 * @param dg
		 * @return
		 */
		public boolean preview()
		{

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

			updateImage();
			repaint();
			notifyChange();

			return true;
		}


		/**
		 * Returns a list of child nodes.
		 * @return a list of chid nodes
		 */
		public ArrayList getChildNodes()
		{
			return new ArrayList();
		}


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

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

			ElementGroupSetPropertiesInFigureElement ep = (ElementGroupSetPropertiesInFigureElement)p;

			SGIFigureElementAxis aElement = SGFigureElementGraph.this.mAxisElement;

			ep.xAxis = aElement.getLocationInCube( this.getXAxis() );
			ep.yAxis = aElement.getLocationInCube( this.getYAxis() );
			ep.zAxis = aElement.getLocationInCube( this.getZAxis() );

			return true;
		}




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

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

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

			ElementGroupSetPropertiesInFigureElement ep = (ElementGroupSetPropertiesInFigureElement)p;

			SGIFigureElementAxis aElement = SGFigureElementGraph.this.mAxisElement;

			this.setXAxis( aElement.getAxisInCube( ep.xAxis ) );
			this.setYAxis( aElement.getAxisInCube( ep.yAxis ) );
			this.setZAxis( aElement.getAxisInCube( ep.zAxis ) );

			return true;
		}


		
		/**
		 * 
		 * @return
		 */
		public String getDataType()
		{
			return SGFigureElementGraph.this.getData( this ).getDataType();
		}



		/**
		 * 
		 * @return
		 */
		public String getInstanceDescription()
		{
			return this.mID + ": "+ this.getDataType() + ": " + this.getName();
		}


		/**
		 * 
		 */
		public boolean writeProperty( final Element el )
		{
			if( super.writeProperty(el) == false )
			{
				return false;
			}
			el.setAttribute( KEY_DATA_TYPE, this.getDataType() );
			return true;
		}



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

		
		/**
		 * Update the locatin of drawing elements.
		 */
		protected abstract boolean updateDrawingElementsLocation( final SGIData 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;
		}



		/**
		 * Synchronize to the given drawing elements.
		 * @param list - list of drawing element
		 * @return whether property changed
		 */
		protected abstract boolean synchronizeDrawingElement( ArrayList list );



		/**
		 * アンドゥを行う
		 */
		public boolean setMementoBackward()
		{
			if( super.setMementoBackward() == false ) return false;

			updateAllDrawingElementsLocation();

			updateImage();
			notifyChangeOnUndo();

			return true;
		}


		/**
		 * リドゥを行う
		 */
		public boolean setMementoForward()
		{
			if( super.setMementoForward() == false ) return false;

			updateAllDrawingElementsLocation();

			updateImage();
			notifyChangeOnUndo();

			return true;
		}



		/**
		 * 
		 *
		 */
		public void notifyToRoot()
		{
			SGFigureElementGraph.this.notifyToRoot();
		}

	}




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

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


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


		/**
		 * 
		 */
		public void dispose()
		{
			super.dispose();

			ArrayList list = this.mElementGroupSetList;
			for( int ii=0; ii<list.size(); ii++ )
			{
				SGElementGroupSet gs = (SGElementGroupSet)list.get(ii);
				gs.dispose();
			}
			
			this.mElementGroupSetList.clear();
			this.mElementGroupSetList = null;
		}


		/**
		 * 
		 */
		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;
			ArrayList list = this.mElementGroupSetList;
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				SGISelectable groupSet = (SGISelectable)list.get(ii);
				groupSet.setSelected(b);
			}
		}


		/**
		 * 
		 * @param b
		 * @return
		 */
		public void setVisible( final boolean b )
		{
			super.setVisible(b);
			ArrayList list = this.mElementGroupSetList;
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)list.get(ii);
				groupSet.setVisible(b);
			}
		}


		/**
		 *
		 */
		public boolean setName( final String name )
		{
			super.setName( name );
			ArrayList list = this.mElementGroupSetList;
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)list.get(ii);
				groupSet.setName(name);
			}
			return true;
		}


		/**
		 * 
		 * @param b
		 * @return
		 */
		public boolean setVisibleInLegend( final boolean b )
		{
			super.setVisibleInLegend(b);
			ArrayList list = this.mElementGroupSetList;
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)list.get(ii);
				groupSet.setVisibleInLegend(b);
			}
			return true;
		}


		/**
		 * 
		 */
		public boolean contains( final int x, final int y )
		{
			ArrayList list = this.mElementGroupSetList;
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)list.get(ii);
				if( groupSet.contains(x,y) )
				{
					return true;
				}
			}
			return false;
		}


		/**
		 * 
		 * @param x
		 * @param y
		 * @return
		 */
		public SGElementGroup getElementGroupAt( final int x, final int y )
		{
			ArrayList list = this.mElementGroupSetList;
			for( int ii=list.size()-1; ii>=0; ii-- )
			{
				ElementGroupSetInGraph groupSet
					= (ElementGroupSetInGraph)list.get(ii);
				SGElementGroup group = groupSet.getElementGroupAt(x,y);
				if( group!=null )
				{
					return group;
				}

			}

			return null;
		}

		/**
		 * 
		 */
		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 onDrawingElement( final int x, final int y )
		{

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

			return false;
		}


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


		/**
		 * 
		 */
		public void setClipFlag( final boolean b )
		{
			super.setClipFlag(b);
			ArrayList list = this.mElementGroupSetList;
			for( int ii=0; ii<list.size(); ii++ )
			{
				ElementGroupSetInGraph grouptSet = (ElementGroupSetInGraph)list.get(ii);
				grouptSet.setClipFlag(b);
			}
		}


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

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

			// 最初のグループからだけ取得する
			final SGElementGroupSet groupSet = (SGElementGroupSet)this.mElementGroupSetList.get(0);
			ArrayList gList = groupSet.getElementGroupList();
			for( int ii=0; ii<gList.size(); ii++ )
			{
				final SGElementGroup group = (SGElementGroup)gList.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 );
			}

			return true;
		}


	}


	/**
	 * @author  okumura
	 */
	public static interface IElementGroupInGraph
	{
		/**
		 * @param b
		 * @uml.property  name="focused"
		 */
		public void setFocused( final boolean b );
		
		/**
		 * @return
		 * @uml.property  name="focused"
		 */
		public boolean isFocused();

		public boolean setElementGroupSet( ElementGroupSetInGraph gs );
	}



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

		ArrayList visibleElementGroupList = new ArrayList();


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


		public void dispose()
		{
			this.visibleElementGroupList.clear();
			this.visibleElementGroupList = null;
		}

		/**
		 * 
		 */
		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;
		}


	}



}

