Bug 8758 - null pointer exception in eclipse core while compiling Java code
Summary: null pointer exception in eclipse core while compiling Java code
Status: RESOLVED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 2.0   Edit
Hardware: PC Windows 2000
: P1 major (vote)
Target Milestone: 2.0 M3   Edit
Assignee: Philipe Mulet CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2002-01-30 11:00 EST by Dmitry B. Khlonin CLA
Modified: 2002-02-12 06:20 EST (History)
0 users

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Dmitry B. Khlonin CLA 2002-01-30 11:00:23 EST
This is the log...


Log: Mon Jan 28 13:54:55 MSK 2002
4 org.eclipse.jdt.ui 1 Internal Error
org.eclipse.core.internal.resources.ResourceException: Errors during build.
    at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:222)
    at
org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java:701)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:1237)
    at
org.eclipse.ui.actions.WorkspaceModifyOperation.run(WorkspaceModifyOperation.java:78)
    at
org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:296)
    at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:249)
    at org.eclipse.jface.wizard.WizardDialog.run(WizardDialog.java:713)
    at
org.eclipse.jdt.internal.ui.wizards.NewProjectCreationWizard.performFinish(NewProjectCreationWizard.java:63)
    at org.eclipse.jface.wizard.WizardDialog.finishPressed(WizardDialog.java:570)
    at org.eclipse.jface.wizard.WizardDialog.buttonPressed(WizardDialog.java:311)
    at org.eclipse.jface.dialogs.Dialog$1.widgetSelected(Dialog.java:344)
    at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:85)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:74)
    at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:637)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:1413)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:1205)
    at org.eclipse.jface.window.Window.runEventLoop(Window.java:536)
    at org.eclipse.jface.window.Window.open(Window.java:523)
    at org.eclipse.ui.actions.NewProjectAction.run(NewProjectAction.java:96)
    at org.eclipse.jface.action.Action.runWithEvent(Action.java:452)
    at
org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:407)
    at
org.eclipse.jface.action.ActionContributionItem.handleWidgetEvent(ActionContributionItem.java:361)
    at
org.eclipse.jface.action.ActionContributionItem.access$0(ActionContributionItem.java:352)
    at
org.eclipse.jface.action.ActionContributionItem$ActionListener.handleEvent(ActionContributionItem.java:47)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:74)
    at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:637)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:1413)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:1205)
    at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:844)
    at org.eclipse.ui.internal.Workbench.run(Workbench.java:827)
    at
org.eclipse.core.internal.boot.InternalBootLoader.run(InternalBootLoader.java:878)
    at org.eclipse.core.boot.BootLoader.run(BootLoader.java:321)
    at java.lang.reflect.Method.invoke(Native Method)
    at org.eclipse.core.launcher.Main.basicRun(Main.java:151)
    at org.eclipse.core.launcher.Main.run(Main.java:502)
    at org.eclipse.core.launcher.Main.main(Main.java:362)
    2 org.eclipse.core.resources 566 Errors during build.
        2=============<children>=============
        2 org.eclipse.jdt.core 75 java.lang.NullPointerException encountered
while running org.eclipse.jdt.internal.core.newbuilder.JavaBuilder.
java.lang.NullPointerException
    at
org.eclipse.jdt.internal.compiler.lookup.BlockScope.emulateOuterAccess(BlockScope.java:347)
    at
org.eclipse.jdt.internal.compiler.ast.SingleNameReference.manageEnclosingInstanceAccessIfNecessary(SingleNameReference.java:558)
    at
org.eclipse.jdt.internal.compiler.ast.SingleNameReference.analyseCode(SingleNameReference.java:119)
    at
org.eclipse.jdt.internal.compiler.ast.MessageSend.analyseCode(MessageSend.java:31)
    at org.eclipse.jdt.internal.compiler.ast.Block.analyseCode(Block.java:36)
    at
org.eclipse.jdt.internal.compiler.ast.IfStatement.analyseCode(IfStatement.java:59)
    at
org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.analyseCode(AbstractMethodDeclaration.java:79)
    at
org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.analyseCode(TypeDeclaration.java:145)
    at
org.eclipse.jdt.internal.compiler.ast.QualifiedAllocationExpression.analyseCode(QualifiedAllocationExpression.java:41)
    at
org.eclipse.jdt.internal.compiler.ast.MessageSend.analyseCode(MessageSend.java:35)
    at
org.eclipse.jdt.internal.compiler.ast.ConstructorDeclaration.analyseCode(ConstructorDeclaration.java:79)
    at
org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.analyseCode(TypeDeclaration.java:208)
    at
org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.analyseCode(TypeDeclaration.java:345)
    at
org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.analyseCode(CompilationUnitDeclaration.java:67)
    at org.eclipse.jdt.internal.compiler.Compiler.process(Compiler.java:456)
    at org.eclipse.jdt.internal.compiler.Compiler.compile(Compiler.java:261)
    at
org.eclipse.jdt.internal.core.newbuilder.AbstractImageBuilder.compile(AbstractImageBuilder.java:212)
    at
org.eclipse.jdt.internal.core.newbuilder.AbstractImageBuilder.compile(AbstractImageBuilder.java:188)
    at
org.eclipse.jdt.internal.core.newbuilder.BatchImageBuilder.build(BatchImageBuilder.java:49)
    at
org.eclipse.jdt.internal.core.newbuilder.JavaBuilder.buildAll(JavaBuilder.java:117)
    at
org.eclipse.jdt.internal.core.newbuilder.JavaBuilder.build(JavaBuilder.java:68)
    at org.eclipse.core.internal.events.BuildManager$2.run(BuildManager.java:358)
    at
org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java:821)
    at org.eclipse.core.runtime.Platform.run(Platform.java:395)
    at
org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:116)
    at
org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:181)
    at
org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:191)
    at org.eclipse.core.internal.events.BuildManager$1.run(BuildManager.java:140)
    at
org.eclipse.core.internal.runtime.InternalPlatform.run(InternalPlatform.java:821)
    at org.eclipse.core.runtime.Platform.run(Platform.java:395)
    at
org.eclipse.core.internal.events.BuildManager.basicBuild(BuildManager.java:154)
    at org.eclipse.core.internal.events.BuildManager.build(BuildManager.java:216)
    at
org.eclipse.core.internal.resources.Workspace.endOperation(Workspace.java:701)
    at org.eclipse.core.internal.resources.Workspace.run(Workspace.java:1237)
    at
org.eclipse.ui.actions.WorkspaceModifyOperation.run(WorkspaceModifyOperation.java:78)
    at
org.eclipse.jface.operation.ModalContext.runInCurrentThread(ModalContext.java:296)
    at org.eclipse.jface.operation.ModalContext.run(ModalContext.java:249)
    at org.eclipse.jface.wizard.WizardDialog.run(WizardDialog.java:713)
    at
org.eclipse.jdt.internal.ui.wizards.NewProjectCreationWizard.performFinish(NewProjectCreationWizard.java:63)
    at org.eclipse.jface.wizard.WizardDialog.finishPressed(WizardDialog.java:570)
    at org.eclipse.jface.wizard.WizardDialog.buttonPressed(WizardDialog.java:311)
    at org.eclipse.jface.dialogs.Dialog$1.widgetSelected(Dialog.java:344)
    at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:85)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:74)
    at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:637)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:1413)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:1205)
    at org.eclipse.jface.window.Window.runEventLoop(Window.java:536)
    at org.eclipse.jface.window.Window.open(Window.java:523)
    at org.eclipse.ui.actions.NewProjectAction.run(NewProjectAction.java:96)
    at org.eclipse.jface.action.Action.runWithEvent(Action.java:452)
    at
org.eclipse.jface.action.ActionContributionItem.handleWidgetSelection(ActionContributionItem.java:407)
    at
org.eclipse.jface.action.ActionContributionItem.handleWidgetEvent(ActionContributionItem.java:361)
    at
org.eclipse.jface.action.ActionContributionItem.access$0(ActionContributionItem.java:352)
    at
org.eclipse.jface.action.ActionContributionItem$ActionListener.handleEvent(ActionContributionItem.java:47)
    at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:74)
    at org.eclipse.swt.widgets.Widget.notifyListeners(Widget.java:637)
    at org.eclipse.swt.widgets.Display.runDeferredEvents(Display.java:1413)
    at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:1205)
    at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:844)
    at org.eclipse.ui.internal.Workbench.run(Workbench.java:827)
    at
org.eclipse.core.internal.boot.InternalBootLoader.run(InternalBootLoader.java:878)
    at org.eclipse.core.boot.BootLoader.run(BootLoader.java:321)
    at java.lang.reflect.Method.invoke(Native Method)
    at org.eclipse.core.launcher.Main.basicRun(Main.java:151)
    at org.eclipse.core.launcher.Main.run(Main.java:502)
    at org.eclipse.core.launcher.Main.main(Main.java:362)
        2=============</children>=============
Comment 1 Philipe Mulet CLA 2002-01-30 14:30:17 EST
Could you produce ?
1- the build number of your Eclipse SDK
2- a source sample exposing this defect 

Comment 2 Philipe Mulet CLA 2002-01-30 14:39:27 EST
There should be an error logged against the offending compilation unit, which 
message should be the stack trace from the .log.

This might help you to narrow the offending source. Good luck !
Comment 3 Dmitry B. Khlonin CLA 2002-01-31 07:24:10 EST
build number: 20011219
Comment 4 Philipe Mulet CLA 2002-02-01 13:13:17 EST
Could you find the source of the offending compilation unit and paste it in ?
This problem occurs while performing the innerclass emulation, and there should 
be a compilation error on this offending unit, which message is the stack trace 
which was in the log.

I am interested in the source of the unit so as to reproduce it.
Comment 5 Dmitry B. Khlonin CLA 2002-02-01 13:28:47 EST
I will give you test class in near future but now I resposible saying that - it
have about 10 inner classes and third level of innerness.
Comment 6 Philipe Mulet CLA 2002-02-02 06:46:49 EST
Seems like a good stress test of innerclasses.
Please just send me the whole thing, I will give it a try.

Comment 7 Philipe Mulet CLA 2002-02-11 04:28:18 EST
Deferring since waiting for a testcase.
Comment 8 Dmitry B. Khlonin CLA 2002-02-11 06:51:13 EST
import java.awt.*;
import java.awt.event.*;
import java.awt.font.*;
import java.awt.geom.*;

import java.util.*;

import javax.swing.*;
import javax.swing.table.*;
import javax.swing.border.*;
import javax.swing.plaf.ToolTipUI;
import javax.swing.plaf.ComponentUI;


public class FTOchartFrame
{

	//constants
	private static final int GENERATE_RANDOM_DEMO = 1;
	private static final int GENERATE_CUSTOM_DEMO = 2;

	/** Field <code>DEMO_LENGTH</code> */
	protected static int DEMO_LENGTH = 5;

	//data

	/** Field <code>parent</code> */
	private java.util.List pieData = new ArrayList();
	private FTOpiePiece[]  workCopy;
	private Map            content = new HashMap();

	//components
	private JSplitPane split;

	/** Field <code>chart</code> */
	protected FTO2dChart chart;

	/** Field <code>legend</code> */
	protected FTOchartLegend legend;

	//formatting

	/** Field <code>legendWidth</code> */
	protected int legendWidth = 100;

	/** Field <code>minSize</code> */
	protected int minSize = 125;

	/** Field <code>m_backgroundColor</code> */
	public static Color m_backgroundColor;

	//options


	/** Field <code>isLegendVisible</code> */
	protected boolean isLegendVisible = true;

	/** Field <code>isEllipseEnabled</code> */
	public boolean isEllipseEnabled = false;

	/** Field <code>isDataModified</code> */
	public boolean  isDataModified   = true;
	private boolean isInitializing   = true;
	private boolean isCallbackNeeded = true;

	//for test

	/** Field <code>test</code> */
	Thread test;

	protected FTOchartFrame( int mode )
	{


		Thread test = new Thread( new Runnable()
		{

			public void run()
			{

				try
				{
					while ( true )
					{
						Thread.currentThread().sleep( 3000 );

						Vector newData =
							new Vector( Arrays
								.asList( generateRandomTest() ) );

						rebuildPie( newData );
					}
				}
				catch ( Exception e ) {}
			}
		} );

		//test.start();
	}

	/**
	 * Constructor <code>FTOchartFrame</code>
	 *
	 *
	 * @param <code>t</code> is of <code>FTOaccJtable</code> type
	 * @param <code>data</code> is of <code>java.util.List</code> type
	 * @param <code>title</code> is of <code>String</code> type
	 * @param <code>conf</code> is of <code>String</code> type
	 *
	 */
	public FTOchartFrame( java.util.List data, String title,
						  String conf )
	{


		initContent();

		//pieData = createDataSet( data );
		rebuildPie( data );

		chart  = createPieChart();
		legend = new FTOchartLegend( this );

		chart.setLegend( legend );

		split = new JSplitPane( JSplitPane.HORIZONTAL_SPLIT, true, chart,
								legend );

		split.setDividerSize( 0 );
		split.setEnabled( false );

		WindowListener wndCloser = new WindowAdapter()
		{

			public void windowClosing( WindowEvent e )
			{

				if ( ( test != null ) && test.isAlive() )
					test.interrupt();


				dispose();
			}

			public void windowActivated( WindowEvent e )
			{

			}
		};

		resetFromConfig();


		SwingUtilities.invokeLater( new Runnable()
		{

			public void run()
			{
			}
		} );
	}

	/**
	 * Method <code>dispose</code>
	 *
	 *
	 */
	public void dispose()
	{

		SwingUtilities.invokeLater( new Runnable()
		{

			public void run()
			{
				destroy();
			}
		} );
	}

	private void destroy()
	{

		if ( chart == null )
			return;

		chart.destroy();
		legend.destroy();

		pieData.clear();

		workCopy = null;

		content.clear();
		split.removeAll();

		split  = null;
		chart  = null;
		legend = null;
	}

	/**
	 * Method <code>reapplyConfig</code>
	 *
	 *
	 * @param <code>conf</code> is of <code>String</code> type
	 *
	 */
	public void reapplyConfig( String conf )
	{


		resetFromConfig();
	}

	/**
	 * Method <code>activateChart</code>
	 *
	 *
	 */
	public void activateChart()
	{


		isCallbackNeeded = false;

	}

	protected void resetFromConfig()
	{

	}

	/**
	 * Method <code>getConfig</code>
	 *
	 *
	 * @return the value of <code>String</code> type
	 *
	 */

	//  public String getConfig()

	protected void centerWindow()
	{


	}

	//Convert FTOpieChartableElement to FTOpiePiece


	private void initContent()
	{
		content = Collections.synchronizedMap( content );
	}

	private static java.util.List createTestData( int mode )
	{

		java.util.List data = null;

		if ( mode == GENERATE_RANDOM_DEMO )
			data = new ArrayList( Arrays.asList( generateRandomTest() ) );
		else if ( mode == GENERATE_CUSTOM_DEMO )
			data = new ArrayList( Arrays.asList( generateCustomTest() ) );

		return Collections.synchronizedList( data );
	}

	/**
	 * Method <code>rebuildPie</code>
	 *
	 *
	 * @param <code>v</code> is of <code>java.util.List</code> type
	 *
	 */
	public void rebuildPie( java.util.List v )
	{

		HashMap update = new HashMap();


		HashSet updates  = new HashSet( update.keySet() );
		Set     current  = content.keySet();
		Set     newItems = new HashSet();
		Set     oldItems = new HashSet();

		//check for new items
		if ( !current.containsAll( updates ) )
		{
			newItems = new HashSet( updates );

			newItems.removeAll( current );

			isDataModified = true;
		}

		//check for removed items
		if ( !updates.containsAll( current ) )
		{
			oldItems = new HashSet( current );

			oldItems.removeAll( updates );

			isDataModified = true;
		}

		//removing old items
		for ( Iterator i = oldItems.iterator(); i.hasNext(); )
		{
			FTOpiePiece p = (FTOpiePiece) content.get( i.next() );

			if ( p.isPieceSelected() )
				selectionChanged( -1, false );

			pieData.remove( p );
		}

		current.removeAll( oldItems );

		//checking for modifications in existing data
		for ( Iterator i = current.iterator(); i.hasNext(); )
		{

		}

		//adding new Items
		for ( Iterator i = newItems.iterator(); i.hasNext(); )
		{

		}

	}

	/**
	 * Method <code>isEmpty</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean isEmpty()
	{

		//return workCopy.length == 0;
		return pieData.size() == 0;
	}

	/**
	 * Method <code>getWorkCopy</code>
	 *
	 *
	 * @return the value of <code>FTOpiePiece[]</code> type
	 *
	 */
	public FTOpiePiece[] getWorkCopy()
	{
		return workCopy;
	}

	/**
	 * Method <code>setWorkCopy</code>
	 *
	 *
	 * @param <code>c</code> is of <code>FTOpiePiece[]</code> type
	 *
	 */
	public void setWorkCopy( FTOpiePiece[] c )
	{

		workCopy = c;

		if ( legend != null )
			legend.m_pieData = c;
	}

	/**
	 * Method <code>getChart</code>
	 *
	 *
	 * @return the value of <code>FTO2dChart</code> type
	 *
	 */
	public FTO2dChart getChart()
	{
		return chart;
	}

	/**
	 * Method <code>getData</code>
	 *
	 *
	 * @return the value of <code>java.util.List</code> type
	 *
	 */
	public java.util.List getData()
	{
		return pieData;
	}

	/**
	 * Method <code>selectionChanged</code>
	 *
	 *
	 * @param <code>index</code> is of <code>int</code> type
	 * @param <code>notify</code> is of <code>boolean</code> type
	 *
	 */
	public void selectionChanged( int index, boolean notify )
	{
		chart.selectionChanged( index, notify );
	}

	/**
	 * Method <code>selectionChanged</code>
	 *
	 *
	 * @param <code>id</code> is of <code>String</code> type
	 *
	 */
	public void selectionChanged( String id )
	{
		chart.selectionChanged( id );
	}

	/**
	 * Method <code>clearSelection</code>
	 *
	 *
	 */
	public void clearSelection()
	{
		chart.clearSelection();
	}

	/**
	 * Method <code>getGradientPaint</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean getGradientPaint()
	{
		return ( chart.getEffectIndex() == chart.EFFECT_GRADIENT );
	}

	/**
	 * Method <code>invertGradient</code>
	 *
	 *
	 */
	public void invertGradient()
	{

		if ( chart.getEffectIndex() == chart.EFFECT_GRADIENT )
			chart.setEffectIndex( chart.EFFECT_PLAIN );
		else
			chart.setEffectIndex( chart.EFFECT_GRADIENT );
	}

	/**
	 * Method <code>getShadows</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean getShadows()
	{
		return chart.getDrawShadow();
	}

	/**
	 * Method <code>invertShadows</code>
	 *
	 *
	 */
	public void invertShadows()
	{

		if ( chart.getDrawShadow() )
			chart.setDrawShadow( false );
		else
			chart.setDrawShadow( true );
	}

	/**
	 * Method <code>getLegend</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean getLegend()
	{
		return isLegendVisible;
	}

	/**
	 * Method <code>invertLegend</code>
	 *
	 *
	 */
	public void invertLegend()
	{

		isLegendVisible = !isLegendVisible;


		setDevLocation();

		//chart.repaint();
	}

	/**
	 * Method <code>getSortingNeeded</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean getSortingNeeded()
	{
		return chart.getSorting();
	}

	/**
	 * Method <code>invertSorting</code>
	 *
	 *
	 */
	public void invertSorting()
	{

		if ( chart.getSorting() )
			chart.setSorting( false );
		else
			chart.setSorting( true );

	}

	/**
	 * Method <code>getEllipseEnabled</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean getEllipseEnabled()
	{
		return isEllipseEnabled;
	}

	/**
	 * Method <code>invertEllipse</code>
	 *
	 *
	 */
	public void invertEllipse()
	{

		isEllipseEnabled = !isEllipseEnabled;

		chart.calculateDimensions();
		chart.repaint();
	}

	/**
	 * Method <code>getGap</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getGap()
	{
		return chart.getGap();
	}

	/**
	 * Method <code>increaseGap</code>
	 *
	 *
	 */
	public void increaseGap()
	{
		chart.increaseGap();
	}

	/**
	 * Method <code>decreaseGap</code>
	 *
	 *
	 */
	public void decreaseGap()
	{
		chart.decreaseGap();
	}

	protected void processComponentEvent( ComponentEvent e )
	{


		setDevLocation();
	}

	protected void setDevLocation()
	{

		if ( split == null )
			return;

		int devider = split.getWidth() - split.getDividerSize();

		if ( ( isLegendVisible ) && ( split.getWidth() >= 2 * legendWidth ) )
			devider -= legendWidth;

		split.setDividerLocation( devider );
	}

	private FTO2dChart createPieChart()
	{

		FTO2dChart chart = new FTO2dChart( FTO2dChart.CHART_DOUBLE_PIE,
										   this );

		chart.setEffectIndex( FTO2dChart.EFFECT_PLAIN );
		chart.setDrawShadow( true );

		return chart;
	}

	//For testing only
	private static FTOinputData[] generateRandomTest()
	{

		int[] yData   = new int[DEMO_LENGTH];
		int[] yDataPL = new int[DEMO_LENGTH];

		for ( int k = 0; k < DEMO_LENGTH; k++ )
		{
			yData[k]   = (int) ( Math.random() * 100 );
			yDataPL[k] = (int) ( Math.random() * 100 );

			boolean sign = ( (int) ( Math.random() * 100 ) ) % 2 == 0;

			if ( k > 0 )
			{
				yData[k]   = ( yData[k - 1] + yData[k] ) / 2;
				yDataPL[k] = ( yDataPL[k - 1] + yDataPL[k] ) / 2;

				if ( sign )
					yDataPL[k] *= -1;
			}
		}

		FTOinputData[] pieData = new FTOinputData[DEMO_LENGTH];

		for ( int k = 0; k < DEMO_LENGTH; k++ )
		{
			FTOinputData piece = new FTOinputData( " piece " + k,
												   " piece " + k, yData[k],
												   yDataPL[k] );

			pieData[k] = piece;
		}

		return pieData;
	}

	//For testing only
	private static FTOinputData[] generateCustomTest()
	{

		int nData = 8;

		DEMO_LENGTH = nData;

		FTOinputData[] data   = new FTOinputData[nData];
		double         volume = 50;
		double         pl     = 0;
		FTOinputData   piece  = new FTOinputData( " piece " + 0,
												  " piece " + 0, volume, pl );

		data[0] = piece;
		volume  = 45;
		pl      = 60;
		piece   = new FTOinputData( " piece " + 1, " piece " + 1, volume,
									pl );
		data[1] = piece;
		volume  = 45;
		pl      = -100;
		piece   = new FTOinputData( " piece " + 2, " piece " + 2, volume,
									pl );
		data[2] = piece;
		volume  = 45;
		pl      = 15;
		piece   = new FTOinputData( " piece " + 3, " piece " + 3, volume,
									pl );
		data[3] = piece;
		volume  = 45;
		pl      = -30;
		piece   = new FTOinputData( " piece " + 4, " piece " + 4, volume,
									pl );
		data[4] = piece;
		volume  = 45;
		pl      = -50;
		piece   = new FTOinputData( " piece " + 5, " piece " + 5, volume,
									pl );
		data[5] = piece;
		volume  = 45;
		pl      = 105;
		piece   = new FTOinputData( " piece " + 6, " piece " + 6, volume,
									pl );
		data[6] = piece;
		volume  = 40;
		pl      = 0;
		piece   = new FTOinputData( " piece " + 7, " piece " + 7, volume,
									pl );
		data[7] = piece;

		return data;
	}

	/**
	 * Method <code>removeWhiteSpacing</code>
	 *
	 *
	 * @param <code>dimension</code> is of <code>double</code> type
	 *
	 */
	public void removeWhiteSpacing( double dimension )
	{

	}

	//For testing only

	/**
	 * Method <code>main</code>
	 *
	 *
	 * @param <code>argv</code> is of <code>String[]</code> type
	 *
	 */
	public static void main( String argv[] )
	{
		new FTOchartFrame( GENERATE_RANDOM_DEMO );
	}
}

class FTOchartLegend extends JPanel
{

	/** Field <code>legend</code> */
	FTOlegendPanel legend;

	/** Field <code>scroll</code> */
	JScrollPane scroll;

	/** Field <code>m_pieData</code> */
	FTOpiePiece[] m_pieData;

	/** Field <code>parent</code> */
	FTOchartFrame parent;

	/** Field <code>isSelectionChanged</code> */
	boolean isSelectionChanged = false;

	/**
	 * Constructor <code>FTOchartLegend</code>
	 *
	 *
	 * @param <code>parent</code> is of <code>FTOchartFrame</code> type
	 *
	 */
	public FTOchartLegend( FTOchartFrame parent )
	{

		super( new BorderLayout() );

		setBackground( FTOchartFrame.m_backgroundColor );

		m_pieData   = parent.getWorkCopy();
		this.parent = parent;
		legend      = new FTOlegendPanel();
		scroll      =
			new JScrollPane( legend,
							 JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
							 JScrollPane.HORIZONTAL_SCROLLBAR_NEVER );

		add( scroll, BorderLayout.CENTER );
	}

	/**
	 * Method <code>destroy</code>
	 *
	 *
	 */
	public void destroy()
	{

		scroll.removeAll();
		removeAll();

		scroll    = null;
		parent    = null;
		legend    = null;
		m_pieData = null;
	}

	/**
	 * Method <code>selectionChanged</code>
	 *
	 *
	 */
	public void selectionChanged()
	{

		isSelectionChanged = true;

		repaint();
	}

	class FTOlegendPanel extends JComponent
	{

		/** Field <code>m_xMargin</code> */
		int m_xMargin = 5;

		/** Field <code>m_yMargin</code> */
		int m_yMargin = 10;

		/** Field <code>rect_width</code> */
		int rect_width = 15;

		/** Field <code>rect_height</code> */
		int rect_height = 10;

		/** Field <code>isHidden</code> */
		boolean isHidden = false;

		/** Field <code>m_lineThickness</code> */
		int m_lineThickness = 3;

		/** Field <code>m_stroke</code> */
		Stroke m_stroke = new BasicStroke( m_lineThickness,
										   BasicStroke.CAP_SQUARE,
										   BasicStroke.JOIN_BEVEL );

		/** Field <code>textFont</code> */
		Font textFont = new Font( "Arial", Font.BOLD, rect_height );

		/** Field <code>selFont</code> */
		Font selFont = new Font( "Arial", Font.BOLD, rect_height + 1 );

		/**
		 * Constructor <code>FTOlegendPanel</code>
		 *
		 *
		 */
		public FTOlegendPanel()
		{

			super();

			addMouseListener( new MouseAdapter()
			{

				public void mousePressed( MouseEvent e )
				{

					if ( ( e.getModifiers() & MouseEvent.BUTTON1_MASK ) > 0 )
					{
						Point p     = e.getPoint();
						int   index = getPieceIndexAtPoint( p );

						parent.selectionChanged( index, true );
					}
				}
			} );
			enableEvents( ComponentEvent.COMPONENT_RESIZED );
		}

		/**
		 * Method <code>processComponentEvent</code>
		 *
		 *
		 * @param <code>e</code> is of <code>ComponentEvent</code> type
		 *
		 */
		public void processComponentEvent( ComponentEvent e )
		{

			if ( getWidth() == 0 )
			{
				if ( !isHidden )
				{
					parent.getChart().repaint();

					isHidden = true;
				}
			}
			else if ( isHidden )
			{
				isHidden = false;

				parent.getChart().repaint();
			}
		}

		protected int getPieceIndexAtPoint( Point p )
		{

			int result = -1;

			if ( m_pieData.length == 0 )
				return result;

			int x =
				( getWidth() - ( 2 * m_xMargin + rect_width + textFont
				.getSize() / 2 * m_pieData[0].getName().length() ) ) / 2;
			int y = m_yMargin;

			for ( int i = 0; i < m_pieData.length; i++ )
			{
				Rectangle2D.Double r = new Rectangle2D.Double( x, y,
										   rect_width, rect_height );

				if ( r.contains( p.getX(), p.getY() ) )
				{
					result = i;

					break;
				}

				y += rect_height + m_yMargin;
			}

			return result;
		}

		/**
		 * Method <code>paintComponent</code>
		 *
		 *
		 * @param <code>g</code> is of <code>Graphics</code> type
		 *
		 */
		public void paintComponent( Graphics g )
		{

			if ( parent.isEmpty() || ( m_pieData.length == 0 ) )
				return;

			Rectangle  selection = null;
			Graphics2D g2        = (Graphics2D) g;

			//setting background
			g2.setColor( FTOchartFrame.m_backgroundColor );

			int x     =
				( getWidth() - ( 2 * m_xMargin + rect_width + textFont
				.getSize() / 2 * m_pieData[0].getName().length() ) ) / 2;
			int y     = m_yMargin;
			int textX = x + rect_width + m_xMargin;

			g2.setFont( textFont );
			g2.setStroke( m_stroke );

			for ( int i = 0; i < m_pieData.length; i++ )
			{
				Rectangle2D.Double r = new Rectangle2D.Double( x, y,
										   rect_width, rect_height );

				g2.setColor( m_pieData[i].getPieceColor() );
				g2.fill3DRect( x, y, rect_width, rect_height, true );

				if ( m_pieData[i].isPieceSelected() )
				{
					g2.setColor( FTO2dChart.m_SelectionColor );
					g2.draw( r );
					g2.setFont( selFont );
					g2.drawString( m_pieData[i].getName().toUpperCase(),
								   textX, y + selFont.getSize() - 2 );
					g2.setFont( textFont );
					g2.setColor( m_pieData[i].getPieceColor() );

					selection = new Rectangle( x, y, rect_width,
											   rect_height );
				}

				g2.drawString( m_pieData[i].getName().toUpperCase(), textX,
							   y + textFont.getSize() );

				y += rect_height + m_yMargin;
			}

			setPreferredSize( new Dimension( getWidth(), y ) );
			revalidate();

			if ( selection != null )
			{
				if ( isSelectionChanged )
				{
					scrollRectToVisible( selection );

					isSelectionChanged = false;
				}
			}
		}
	}
}

class FTO2dChart extends JPanel
{

	/** Field <code>CHART_DOUBLE_PIE</code> */
	public static final int CHART_DOUBLE_PIE = 1;

	/** Field <code>EFFECT_PLAIN</code> */
	public static final int EFFECT_PLAIN = 0;

	/** Field <code>EFFECT_GRADIENT</code> */
	public static final int EFFECT_GRADIENT = 1;

	/** Field <code>EFFECT_IMAGE</code> */
	public static final int EFFECT_IMAGE = 2;

	/** Field <code>MAX_GAP</code> */
	final static double MAX_GAP = 0.055;

	/** Field <code>MIN_GAP</code> */
	final static double MIN_GAP = 0;

	/** Field <code>m_chartType</code> */
	protected int m_chartType = CHART_DOUBLE_PIE;

	/** Field <code>parent</code> */
	protected FTOchartFrame parent;

	/** Field <code>m_title</code> */
	protected JLabel m_title;

	/** Field <code>m_chart</code> */
	protected FTOchartPanel m_chart;

	/** Field <code>m_xData</code> */
	protected int[] m_xData;

	/** Field <code>m_yData</code> */
	protected int[] m_yData;

	/** Field <code>m_xMin</code> */
	protected int m_xMin;

	/** Field <code>m_xMax</code> */
	protected int m_xMax;

	/** Field <code>m_yMin</code> */
	protected int m_yMin;

	/** Field <code>m_yMax</code> */
	protected int         m_yMax;
	private FTOpiePiece[] m_model = new FTOpiePiece[0];

	/** Field <code>m_effectIndex</code> */
	protected int m_effectIndex = EFFECT_PLAIN;

	/** Field <code>m_stroke</code> */
	protected Stroke m_stroke;

	/** Field <code>m_sel_stroke</code> */
	protected Stroke m_sel_stroke;

	/** Field <code>m_foregroundImage</code> */
	protected Image m_foregroundImage;

	/** Field <code>m_lineColor</code> */
	public static Color m_lineColor = Color.black;

	/** Field <code>m_SelectionColor</code> */
	public static Color m_SelectionColor = Color.white;

	/** Field <code>m_drawShadow</code> */
	protected boolean m_drawShadow = false;

	/** Field <code>m_lineThickness</code> */
	protected float m_lineThickness = (float) 1.6;

	/** Field <code>isRotated</code> */
	protected boolean isRotated = true;

	/** Field <code>isSortingNeeded</code> */
	protected boolean isSortingNeeded = true;

	/** Field <code>legend</code> */
	protected FTOchartLegend legend;

	/**
	 * Constructor <code>FTO2dChart</code>
	 *
	 *
	 * @param <code>type</code> is of <code>int</code> type
	 * @param <code>parent</code> is of <code>FTOchartFrame</code> type
	 *
	 */
	public FTO2dChart( int type, FTOchartFrame parent )
	{

		super( new BorderLayout() );

		setBackground( FTOchartFrame.m_backgroundColor );

		this.parent = parent;
		m_chartType = type;

		if ( type == CHART_DOUBLE_PIE )
		{
			m_stroke     = new BasicStroke( m_lineThickness,
											BasicStroke.CAP_ROUND,
											BasicStroke.JOIN_ROUND );
			m_sel_stroke = new BasicStroke( m_lineThickness + 1,
											BasicStroke.CAP_ROUND,
											BasicStroke.JOIN_ROUND );
		}

		m_chart = new FTOchartPanel();

		add( m_chart, BorderLayout.CENTER );
	}

	/**
	 * Method <code>destroy</code>
	 *
	 *
	 */
	public void destroy()
	{

		m_chart.destroy();
		removeAll();

		parent  = null;
		m_chart = null;
		legend  = null;
		m_model = null;
	}

	/**
	 * Method <code>setGap</code>
	 *
	 *
	 * @param <code>d</code> is of <code>double</code> type
	 *
	 */
	public void setGap( double d )
	{
		m_chart.setGap( d );
	}

	/**
	 * Method <code>getGap</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getGap()
	{
		return m_chart.getGap();
	}

	/**
	 * Method <code>calculateDimensions</code>
	 *
	 *
	 */
	public void calculateDimensions()
	{
		m_chart.calcDimensions();
	}

	/**
	 * Method <code>increaseGap</code>
	 *
	 *
	 */
	public void increaseGap()
	{

		m_chart.increaseGap();

		m_chart.isGap = true;
	}

	/**
	 * Method <code>decreaseGap</code>
	 *
	 *
	 */
	public void decreaseGap()
	{

		m_chart.decreaseGap();

		m_chart.isGap = true;
	}

	/**
	 * Method <code>selectionChanged</code>
	 *
	 *
	 * @param <code>index</code> is of <code>int</code> type
	 * @param <code>notify</code> is of <code>boolean</code> type
	 *
	 */
	public void selectionChanged( int index, boolean notify )
	{
		m_chart.selectionChanged( index, notify );
	}

	/**
	 * Method <code>clearSelection</code>
	 *
	 *
	 */
	public void clearSelection()
	{
		m_chart.clearSelection();
	}

	/**
	 * Method <code>selectionChanged</code>
	 *
	 *
	 * @param <code>id</code> is of <code>String</code> type
	 *
	 */
	public void selectionChanged( String id )
	{

		int index = -1;

		for ( int i = 0; i < m_model.length; i++ )
		{
			if ( m_model[i].getId().equals( id ) )
			{
				index = i;

				break;
			}
		}

		selectionChanged( index, false );
	}

	/**
	 * Method <code>guiCallback</code>
	 *
	 *
	 * @param <code>index</code> is of <code>int</code> type
	 *
	 */
	public void guiCallback( int index )
	{
	}

	/**
	 * Method <code>getSorting</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean getSorting()
	{
		return isSortingNeeded;
	}

	/**
	 * Method <code>getChartHeight</code>
	 *
	 *
	 * @return the value of <code>int</code> type
	 *
	 */
	public int getChartHeight()
	{
		return m_chart.getHeight();
	}

	/**
	 * Method <code>setSorting</code>
	 *
	 *
	 * @param <code>par</code> is of <code>boolean</code> type
	 *
	 */
	public void setSorting( boolean par )
	{

		isSortingNeeded  = par;
		isRotated        = true;
		m_chart.isSorted = false;
	}

	/**
	 * Method <code>setLegend</code>
	 *
	 *
	 * @param <code>legend</code> is of <code>FTOchartLegend</code> type
	 *
	 */
	public void setLegend( FTOchartLegend legend )
	{
		this.legend = legend;
	}

	/**
	 * Method <code>setEffectIndex</code>
	 *
	 *
	 * @param <code>effectIndex</code> is of <code>int</code> type
	 *
	 */
	public void setEffectIndex( int effectIndex )
	{

		m_effectIndex = effectIndex;

		if ( m_effectIndex == EFFECT_GRADIENT )
			m_chart.isGradient = true;

		repaint();
	}

	/**
	 * Method <code>getEffectIndex</code>
	 *
	 *
	 * @return the value of <code>int</code> type
	 *
	 */
	public int getEffectIndex()
	{
		return m_effectIndex;
	}

	/**
	 * Method <code>setStroke</code>
	 *
	 *
	 * @param <code>stroke</code> is of <code>Stroke</code> type
	 *
	 */
	public void setStroke( Stroke stroke )
	{

		m_stroke = stroke;

		m_chart.repaint();
	}

	/**
	 * Method <code>setForegroundImage</code>
	 *
	 *
	 * @param <code>img</code> is of <code>Image</code> type
	 *
	 */
	public void setForegroundImage( Image img )
	{

		m_foregroundImage = img;

		repaint();
	}

	/**
	 * Method <code>getForegroundImage</code>
	 *
	 *
	 * @return the value of <code>Image</code> type
	 *
	 */
	public Image getForegroundImage()
	{
		return m_foregroundImage;
	}

	/**
	 * Method <code>getStroke</code>
	 *
	 *
	 * @return the value of <code>Stroke</code> type
	 *
	 */
	public Stroke getStroke()
	{
		return m_stroke;
	}

	/**
	 * Method <code>setLineColor</code>
	 *
	 *
	 * @param <code>c</code> is of <code>Color</code> type
	 *
	 */
	public void setLineColor( Color c )
	{

		m_lineColor = c;

		m_chart.repaint();
	}

	/**
	 * Method <code>getLineColor</code>
	 *
	 *
	 * @return the value of <code>Color</code> type
	 *
	 */
	public Color getLineColor()
	{
		return m_lineColor;
	}

	/**
	 * Method <code>setDrawShadow</code>
	 *
	 *
	 * @param <code>drawShadow</code> is of <code>boolean</code> type
	 *
	 */
	public void setDrawShadow( boolean drawShadow )
	{

		m_drawShadow = drawShadow;

		m_chart.repaint();
	}

	/**
	 * Method <code>getDrawShadow</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean getDrawShadow()
	{
		return m_drawShadow;
	}

	class FTOchartPanel extends JComponent implements ActionListener
	{

		/** Field <code>m_xMargin</code> */
		int m_xMargin = 10;

		/** Field <code>m_yMargin</code> */
		int m_yMargin = 10;

		/** Field <code>m_pieGap</code> */
		double m_pieGap;

		//min 0.025 max 0.055

		/** Field <code>gapStep</code> */
		double gapStep = 0.01;

		/** Field <code>min_gap</code> */
		double min_gap = 0.025;

		/** Field <code>m_gapCoefficient</code> */
		double m_gapCoefficient = 0;    //0.035;

		/** Field <code>m_ringCoefficient</code> */
		final double m_ringCoefficient = 0.1;

		/** Field <code>m_x</code> */
		int m_x;

		/** Field <code>m_y</code> */
		int m_y;

		/** Field <code>m_w</code> */
		int m_w;

		/** Field <code>m_h</code> */
		int m_h;

		/** Field <code>lastSelectedPiece</code> */
		int lastSelectedPiece = -1;

		/** Field <code>startIndex</code> */
		int startIndex = 0;

		/** Field <code>isSorted</code> */
		boolean isSorted = false;

		/** Field <code>isResized</code> */
		boolean isResized = true;

		/** Field <code>isGradient</code> */
		boolean isGradient = false;

		/** Field <code>isGap</code> */
		boolean              isGap           = false;
		private final String ACTION_SHADOWS  = "shadows";
		private final String ACTION_ELLIPSE  = "ellipse";
		private final String ACTION_PAINTING = "gradient";
		private final String ACTION_SORTING  = "sorting";
		private final String ACTION_GAP_UP   = "gap+";
		private final String ACTION_GAP_DOWN = "gap-";
		private final String ACTION_LEGEND   = "legend";

		/** Field <code>popup</code> */
		protected FTOchartPopupMenu popup;

		/** Field <code>comp</code> */
		protected Comparator comp = new FTOchartComparator();

		FTOchartPanel()
		{

			enableEvents( ComponentEvent.COMPONENT_RESIZED );

			popup = new FTOchartPopupMenu( parent );

			registerKeyboardActions();
			restoreOriginal();


			//Initializing tooltips
			setToolTipText( "" );
		}

		/**
		 * Method <code>destroy</code>
		 *
		 *
		 */
		public void destroy()
		{

			popup.destroy();
			removeAll();

			comp  = null;
			popup = null;
		}

		/**
		 * Method <code>setGap</code>
		 *
		 *
		 * @param <code>d</code> is of <code>double</code> type
		 *
		 */
		public void setGap( double d )
		{
			m_gapCoefficient = d;
		}

		/**
		 * Method <code>getGap</code>
		 *
		 *
		 * @return the value of <code>double</code> type
		 *
		 */
		public double getGap()
		{
			return m_gapCoefficient;
		}

		/**
		 * Method <code>createToolTip</code>
		 *
		 *
		 * @return the value of <code>JToolTip</code> type
		 *
		 */
		public JToolTip createToolTip()
		{

			JToolTip tip = new FTOtoolTip();

			tip.setComponent( this );

			return tip;
		}

		private synchronized void restoreOriginal()
		{

			m_model = new FTOpiePiece[0];

			java.util.List data = parent.getData();
			java.util.List copy = new ArrayList( data );

			m_model = (FTOpiePiece[]) copy.toArray( m_model );

			parent.setWorkCopy( m_model );
		}

		private void registerKeyboardActions()
		{

			KeyStroke ks = KeyStroke.getKeyStroke( KeyEvent.VK_S, 0, false );

			registerKeyboardAction( this, ACTION_SHADOWS, ks,
									WHEN_IN_FOCUSED_WINDOW );

			ks = KeyStroke.getKeyStroke( KeyEvent.VK_E, 0, false );

			registerKeyboardAction( this, ACTION_ELLIPSE, ks,
									WHEN_IN_FOCUSED_WINDOW );

			/*ks = KeyStroke.getKeyStroke( KeyEvent.VK_O, 0, false );
			registerKeyboardAction( this, ACTION_SORTING, ks, WHEN_IN_FOCUSED_WINDOW );*/
			ks = KeyStroke.getKeyStroke( KeyEvent.VK_G, 0, false );

			registerKeyboardAction( this, ACTION_PAINTING, ks,
									WHEN_IN_FOCUSED_WINDOW );

			ks = KeyStroke.getKeyStroke( KeyEvent.VK_UP, 0, false );

			registerKeyboardAction( this, ACTION_GAP_UP, ks,
									WHEN_IN_FOCUSED_WINDOW );

			ks = KeyStroke.getKeyStroke( KeyEvent.VK_DOWN, 0, false );

			registerKeyboardAction( this, ACTION_GAP_DOWN, ks,
									WHEN_IN_FOCUSED_WINDOW );

			ks = KeyStroke.getKeyStroke( KeyEvent.VK_L, 0 );

			registerKeyboardAction( this, ACTION_LEGEND, ks,
									WHEN_IN_FOCUSED_WINDOW );
		}

		/**
		 * Method <code>actionPerformed</code>
		 *
		 *
		 * @param <code>e</code> is of <code>ActionEvent</code> type
		 *
		 */
		public void actionPerformed( ActionEvent e )
		{

			if ( parent.isEmpty() )
				return;

			String command = e.getActionCommand();

			if ( command.equals( ACTION_ELLIPSE ) )
				parent.invertEllipse();
			else if ( command.equals( ACTION_GAP_DOWN ) )
				parent.decreaseGap();
			else if ( command.equals( ACTION_GAP_UP ) )
				parent.increaseGap();
			else if ( command.equals( ACTION_LEGEND ) )
				parent.invertLegend();
			else if ( command.equals( ACTION_PAINTING ) )
				parent.invertGradient();
			else if ( command.equals( ACTION_SHADOWS ) )
				parent.invertShadows();
			else if ( command.equals( ACTION_SORTING ) )
				parent.invertSorting();
		}

		/**
		 * Method <code>processMouseEvent</code>
		 *
		 *
		 * @param <code>e</code> is of <code>MouseEvent</code> type
		 *
		 */
		public void processMouseEvent( MouseEvent e )
		{

			if ( e.isPopupTrigger() )
			{
				popup.show( this, e.getX(), e.getY() );
			}
			else
			{
				if ( ( e.getID() == MouseEvent.MOUSE_PRESSED )
						&& ( e.getModifiers() & MouseEvent.BUTTON1_MASK )
						   > 0 )
				{
					Point p     = e.getPoint();
					int   index = getPieceIndexAtPoint( p );

					selectionChanged( index, true );
				}
			}

			super.processMouseEvent( e );
		}

		/**
		 * Method <code>increaseGap</code>
		 *
		 *
		 */
		public void increaseGap()
		{

			if ( (int) ( m_gapCoefficient * 1000 )
					== (int) ( MAX_GAP * 1000 ) )
				return;

			m_gapCoefficient += gapStep;

			if ( m_gapCoefficient < min_gap )
				m_gapCoefficient = min_gap;

			repaint();
		}

		/**
		 * Method <code>decreaseGap</code>
		 *
		 *
		 */
		public void decreaseGap()
		{

			m_gapCoefficient -= gapStep;

			if ( m_gapCoefficient < min_gap )
				m_gapCoefficient = 0;

			repaint();
		}

		/**
		 * Method <code>selectionChanged</code>
		 *
		 *
		 * @param <code>index</code> is of <code>int</code> type
		 * @param <code>isNotifyNeeded</code> is of <code>boolean</code> type
		 *
		 */
		public void selectionChanged( int index, boolean isNotifyNeeded )
		{

			if ( index < 0 )
				return;

			if ( lastSelectedPiece == index )
				return;

			if ( lastSelectedPiece >= 0 )
				m_model[lastSelectedPiece].setPieceSelected( false );

			lastSelectedPiece = index;

			m_model[index].setPieceSelected( true );
			legend.selectionChanged();

			isRotated = true;

			repaint();

			if ( isNotifyNeeded )
				guiCallback( index );
		}

		/**
		 * Method <code>clearSelection</code>
		 *
		 *
		 */
		public void clearSelection()
		{

			if ( lastSelectedPiece < 0 )
				return;

			m_model[lastSelectedPiece].setPieceSelected( false );

			lastSelectedPiece = -1;

			legend.selectionChanged();
			repaint();
		}

		protected void arrangePieces()
		{

			isSorted = false;

			if ( !isSortingNeeded )
				restoreOriginal();
			else
			{
				java.util.List copy = Arrays.asList( m_model );

				Collections.sort( copy, comp );
			}

			legend.repaint();
		}

		protected void resetColors()
		{

			if ( m_model.length == 0 )
				return;

			int plCount     = 0;
			int profitCount = 0;
			int zeroCount   = 0;

			for ( int i = 0; i < m_model.length; i++ )
			{
				if ( m_model[i].isProfitPiece() > 0 )
					profitCount++;
				else if ( m_model[i].isProfitPiece() < 0 )
					plCount++;
				else
					zeroCount++;
			}

			int            profitStep = 155 / ( profitCount + 1 );
			int            plStep     = 200 / ( plCount + 1 );
			int            zeroStep   = 200 / ( zeroCount + 1 );
			java.util.List copy       = Arrays.asList( m_model );
			ArrayList      l          = new ArrayList( copy );

			Collections.sort( l, comp );

			//pl < 0 - red
			for ( int i = 0; i < plCount; i++ )
			{
				FTOpiePiece p = (FTOpiePiece) l.get( i );
				Color       c = new Color( 255 - plStep * i, 0, 0 );

				p.setGradientStart( new Color( 255, 0, 0 ) );
				p.setPieceColor( c );
			}

			//pl = 0 - blue
			for ( int i = plCount; i < plCount + zeroCount; i++ )
			{
				FTOpiePiece p = (FTOpiePiece) l.get( i );
				Color       c = new Color( 0, 0,
										   255 - zeroStep * ( i - plCount ) );

				p.setGradientStart( new Color( 0, 0, 255 ) );
				p.setPieceColor( c );
			}

			// pl > 0 - green
			for ( int i = plCount + zeroCount; i < m_model.length; i++ )
			{
				FTOpiePiece p = (FTOpiePiece) l.get( i );
				Color       c = new Color( 0, 100
										   + profitStep
											 * ( i - plCount
												 - zeroCount ), 0 );

				p.setGradientStart( new Color( 0, 255, 0 ) );
				p.setPieceColor( c );
			}
		}

		private int getPieceIndexAtPoint( Point p )
		{

			int result = -1;

			for ( int i = 0; i < m_model.length; i++ )
			{
				if ( ( m_model[i].getShape() != null )
						&& ( m_model[i].getShapePL() != null ) )
				{
					if ( m_model[i].getShape().contains( p.getX(), p.getY() )
							|| m_model[i].getShapePL().contains( p.getX(),
								p.getY() ) )
					{
						result = i;

						break;
					}
				}
			}

			return result;
		}

		protected void processComponentEvent( ComponentEvent e )
		{

			calcDimensions();

			isResized = true;

			repaint();
		}

		/**
		 * Method <code>calcDimensions</code>
		 *
		 *
		 */
		public void calcDimensions()
		{

			if ( parent == null )
				return;

			Dimension d = getSize();

			m_x = m_xMargin;
			m_y = m_yMargin;
			m_w = d.width - 2 * m_xMargin;
			m_h = d.height - 2 * m_yMargin;

			if ( !parent.isEllipseEnabled
					&& ( Math.abs( d.getWidth() - d.getHeight() ) > 1 ) )
				parent.removeWhiteSpacing( Math.min( d.getWidth(),
													 d.getHeight() ) );
		}

		/**
		 * Method <code>getToolTipText</code>
		 *
		 *
		 * @param <code>event</code> is of <code>MouseEvent</code> type
		 *
		 * @return the value of <code>String</code> type
		 *
		 */
		public String getToolTipText( MouseEvent event )
		{

			int    index  = getPieceIndexAtPoint( new Point( event.getX(),
								event.getY() ) );
			String result = null;

			if ( index >= 0 )
			{
			}

			return result;
		}

		/**
		 * Method <code>paint</code>
		 *
		 *
		 * @param <code>g</code> is of <code>Graphics</code> type
		 *
		 */
		public void paint( Graphics g )
		{

			if ( parent.isEmpty() )
				return;

			Graphics2D g2 = (Graphics2D) g;

			//setting graphics quality options
			setGraphicOptions( g2 );

			//calculating gap absolute value
			m_pieGap = m_gapCoefficient * Math.min( m_w, m_h );

			//setting stroke
			if ( m_stroke != null )
				g2.setStroke( m_stroke );

			//setting background
			g2.setColor( FTOchartFrame.m_backgroundColor );
			g2.fill( g2.getClip() );

			switch ( m_chartType )
			{

				case CHART_DOUBLE_PIE :
				{

					//Calculating angles
					if ( parent.isDataModified )
					{
						restoreOriginal();
						convertToAngles();

						isRotated             = true;
						parent.isDataModified = false;
					}

					if ( !isSorted )
						arrangePieces();

					//initializing painting area
					double ww = m_w - 2 * m_pieGap;
					double hh = m_h - 2 * m_pieGap;

					if ( !parent.isEllipseEnabled )
					{
						ww = Math.min( ww, hh );
						hh = ww;
					}

					//if ( m_drawShadow )
					//{
					ww -= m_pieGap;
					hh -= m_pieGap;

					//}
					//recalculating angles, rotating if needed
					if ( isRotated )
						calculateAngles();

					//creating shapes, if needed
					if ( isRotated || isResized || isGradient || isGap )
					{
						for ( int k = 0; k < m_model.length; k++ )
						{
							createPiePiece( k, ww, hh );
						}

						isRotated  = false;
						isResized  = false;
						isGradient = false;
						isGap      = false;
					}

					break;
				}
			}

			//drawing shadows, if needed
			if ( m_drawShadow )
				for ( int k = 0; k < m_model.length; k++ )
				{
					g2.setColor( Color.gray );
					g2.fill( m_model[k].getShadow() );
				}

			lastSelectedPiece = -1;

			//drawing pieces ( exclude selected )
			for ( int k = 0; k < m_model.length; k++ )
			{
				if ( !m_model[k].isPieceSelected() )
				{

					//setting gradient paint
					if ( ( m_effectIndex == EFFECT_GRADIENT )
							&& ( m_model[k].getGradient() != null ) )
					{
						g2.setPaint( m_model[k].getGradient() );
						g2.fill( m_model[k].getShape() );
						g2.fill( m_model[k].getShapePL() );
					}

					//setting plain color
					else if ( ( m_effectIndex == EFFECT_PLAIN )
							  && ( m_model[k].getPieceColor() != null ) )
					{
						g2.setColor( m_model[k].getPieceColor() );
						g2.fill( m_model[k].getShape() );
						g2.fill( m_model[k].getShapePL() );
					}

					//if no gap, then border painting needed
					if ( m_gapCoefficient == 0 )
						g2.setColor( m_lineColor );

					g2.draw( m_model[k].getShape() );
					g2.draw( m_model[k].getShapePL() );
				}
				else
					lastSelectedPiece = k;
			}

			//drawing selected piece
			if ( lastSelectedPiece >= 0 )
			{

				//setting stroke
				if ( m_stroke != null )
					g2.setStroke( m_sel_stroke );

				int k = lastSelectedPiece;

				//setting gradient paint
				if ( ( m_effectIndex == EFFECT_GRADIENT )
						&& ( m_model[k].getGradient() != null ) )
				{
					g2.setPaint( m_model[k].getGradient() );
					g2.fill( m_model[k].getShape() );
					g2.fill( m_model[k].getShapePL() );
				}

				//setting plain color
				else if ( ( m_effectIndex == EFFECT_PLAIN )
						  && ( m_model[k].getPieceColor() != null ) )
				{
					g2.setColor( m_model[k].getPieceColor() );
					g2.fill( m_model[k].getShape() );
					g2.fill( m_model[k].getShapePL() );
				}

				//setting selection color
				g2.setColor( m_SelectionColor );
				g2.draw( m_model[k].getShape() );
				g2.draw( m_model[k].getShapePL() );
			}
		}

		/**
		 * Method <code>updateUI</code>
		 *
		 *
		 */
		public void updateUI()
		{

			super.updateUI();

			if ( popup == null )
				return;

			SwingUtilities.updateComponentTreeUI( popup );
			popup.pack();
			SwingUtilities.invokeLater( new Runnable()
			{

				public void run()
				{
					setupFontsAndColors();
				}
			} );
		}

		/**  */
		public void setupFontsAndColors() {}

		protected void convertToAngles()
		{

			double sum   = 0;
			double sumPL = 0;

			for ( int k = 0; k < m_model.length; k++ )
			{
				m_model[k].setVolumeAngle( Math.max( m_model[k].getVolume(),
													 0 ) );
				m_model[k].setPLAngle( Math.max( m_model[k].getPL(), 0 ) );

				sum   += m_model[k].getVolumeAngle();
				sumPL += m_model[k].getPLAngle();
			}

			for ( int k = 0; k < m_model.length; k++ )
			{
				m_model[k].setVolumeAngle( m_model[k].getVolumeAngle()
										   * 360.0 / sum );
				m_model[k].setPLAngle( m_model[k].getPLAngle() * 360.0
									   / sumPL );
			}

			isSorted = false;

			resetColors();
		}

		protected void calculateAngles()
		{

			double start  = 0.0;
			double finish = 0.0;

			startIndex        = 0;
			lastSelectedPiece = -1;

			for ( int i = 0; i < m_model.length; i++ )
			{
				if ( m_model[i].isPieceSelected() )
				{
					lastSelectedPiece = i;

					break;
				}
			}

			if ( ( lastSelectedPiece >= 0 )
					&& ( m_model[lastSelectedPiece].isProfitPiece() != 0 ) )
				startIndex = lastSelectedPiece;

			for ( int k = 0; k < m_model.length; k++ )
			{
				finish = start + m_model[k].getVolumeAngle();

				m_model[k].setStart( start );
				m_model[k].setFinish( finish );

				start = finish;
			}

			calculatePLangles();
		}

		protected void calculatePLangles()
		{

			double startPL  = m_model[startIndex].getStart();
			double finishPL = 0.0;

			for ( int k = startIndex; k < m_model.length; k++ )
			{
				finishPL = startPL + m_model[k].getPLAngle();

				m_model[k].setStartPL( startPL );
				m_model[k].setFinishPL( finishPL );

				startPL = finishPL;
			}

			for ( int k = 0; k < startIndex; k++ )
			{
				finishPL = startPL + m_model[k].getPLAngle();

				m_model[k].setStartPL( startPL );
				m_model[k].setFinishPL( finishPL );

				startPL = finishPL;
			}
		}

		protected void createPiePiece( int index, double ww, double hh )
		{

			//calculating ring width
			double ringWidthX = ww * m_ringCoefficient;
			double ringWidthY = hh * m_ringCoefficient;

			//calculating angles
			double f1   = Math.min( 90 - m_model[index].getStart(),
									90 - m_model[index].getFinish() );
			double f2   = Math.max( 90 - m_model[index].getStart(),
									90 - m_model[index].getFinish() );
			double f1PL = Math.min( 90 - m_model[index].getStartPL(),
									90 - m_model[index].getFinishPL() );
			double f2PL = Math.max( 90 - m_model[index].getStartPL(),
									90 - m_model[index].getFinishPL() );

			//creating shapes
			Arc2D.Double shp  = new Arc2D.Double( m_x, m_y, ww, hh, f2PL,
												  ( -1 ) * ( f2PL - f1PL ),
												  Arc2D.PIE );
			Shape        shp2 = new Ellipse2D.Double( m_x + ringWidthX,
													  m_y + ringWidthY,
													  ww - 2 * ringWidthX,
													  hh - 2 * ringWidthY );
			Shape        shp3 = new Arc2D.Double( m_x + ringWidthX
												  + m_pieGap,
												  m_y + ringWidthY
												  + m_pieGap,
												  ww - 2
												  * ( m_pieGap + ringWidthX ),
												  hh - 2
												  * ( m_pieGap + ringWidthY ),
												  f2, ( -1 ) * ( f2 - f1 ),
												  Arc2D.PIE );
			Area         a    = new Area( shp );
			Area         a2   = new Area( shp2 );
			Area         a3   = new Area( shp3 );

			a.subtract( a2 );

			double f   = ( f1 + f2 ) / 2 * Math.PI / 180;
			double fPL = ( f1PL + f2PL ) / 2 * Math.PI / 180;

			if ( m_effectIndex == EFFECT_GRADIENT )
			{
				Point  center = new Point( (int) shp.getCenterX(),
										   (int) shp.getCenterY() );
				double radius = Math.min( ww / 2, hh / 2 );
				Point  finish =
					new Point( (int) ( center.x + radius * Math.cos( f ) ),
							   (int) ( center.y - radius * Math.sin( f ) ) );

				m_model[index].setGradient( new GradientPaint( center.x,
						center.y, m_model[index].getGradientStart(),
						finish.x, finish.y, m_model[index].getPieceColor(),
						true ) );
			}

			//translating if needed
			if ( m_pieGap != 0 )
			{
				AffineTransform s1 =
					AffineTransform.getTranslateInstance( m_pieGap
														  * Math.cos( fPL ),
														  -m_pieGap
														  * Math.sin( fPL ) );

				a = new Area( s1.createTransformedShape( a ) );

				AffineTransform s2 =
					AffineTransform.getTranslateInstance( m_pieGap
														  * Math.cos( f ),
														  -m_pieGap
														  * Math.sin( f ) );

				a3 = new Area( s2.createTransformedShape( a3 ) );
			}

			if ( m_pieGap != 0 )
			{
				AffineTransform s3 =
					AffineTransform.getTranslateInstance( m_pieGap,
														  m_pieGap );

				a3 = new Area( s3.createTransformedShape( a3 ) );
				a  = new Area( s3.createTransformedShape( a ) );
			}

			m_model[index].setShape( new Area( a3 ) );
			m_model[index].setShapePL( a );
			a3.add( a );

			//creating shadows if needed
			//if ( m_drawShadow )
			//{
			AffineTransform s0     =
				AffineTransform.getTranslateInstance( m_pieGap, m_pieGap );
			Shape           shadow = s0.createTransformedShape( a3 );

			m_model[index].setShadow( shadow );

			//}
		}

		protected void fillByImage( Graphics2D g2, Shape shape, int xOffset )
		{

			if ( m_foregroundImage == null )
				return;

			int wImg = m_foregroundImage.getWidth( this );
			int hImg = m_foregroundImage.getHeight( this );

			if ( ( wImg <= 0 ) || ( hImg <= 0 ) )
				return;

			g2.setClip( shape );

			Rectangle bounds = shape.getBounds();

			for ( int xx = bounds.x + xOffset; xx < bounds.x + bounds.width;
					xx += wImg )
				for ( int yy = bounds.y; yy < bounds.y + bounds.height;
						yy += hImg )
					g2.drawImage( m_foregroundImage, xx, yy, this );
		}

		protected void setGraphicOptions( Graphics2D g )
		{

			g.setRenderingHint( RenderingHints.KEY_ANTIALIASING,
								RenderingHints.VALUE_ANTIALIAS_ON );
			g.setRenderingHint( RenderingHints.KEY_COLOR_RENDERING,
								RenderingHints.VALUE_COLOR_RENDER_QUALITY );
			g.setRenderingHint( RenderingHints.KEY_ALPHA_INTERPOLATION,
								RenderingHints
									.VALUE_ALPHA_INTERPOLATION_QUALITY );
			g.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS,
								RenderingHints.VALUE_FRACTIONALMETRICS_ON );
			g.setRenderingHint( RenderingHints.KEY_STROKE_CONTROL,
								RenderingHints.VALUE_STROKE_PURE );
			g.setRenderingHint( RenderingHints.KEY_RENDERING,
								RenderingHints.VALUE_RENDER_QUALITY );
			g.setRenderingHint( RenderingHints.KEY_TEXT_ANTIALIASING,
								RenderingHints.VALUE_TEXT_ANTIALIAS_ON );
			g.setRenderingHint( RenderingHints.KEY_INTERPOLATION,
								RenderingHints
									.VALUE_INTERPOLATION_NEAREST_NEIGHBOR );
			g.setRenderingHint( RenderingHints.KEY_DITHERING,
								RenderingHints.VALUE_DITHER_ENABLE );
		}
	}
}

class FTOpiePiece
{

	private boolean       isSelected = false;
	private int           isProfit;
	private double        angleOfVolume;
	private double        angleOfPL;
	private double        volume;
	private double        pl;
	private Color         pieceColor;
	private GradientPaint gradient;
	private String        pieceName;
	private String        id;
	private double        start;
	private double        finish;
	private double        startPL;
	private double        finishPL;
	private Shape         instance;
	private Shape         instancePL;
	private Shape         shadow;
	private boolean       isRepaintNeeded = false;
	private Color         gradStart       = Color.white;
	private Object        source;

	/**
	 * Constructor <code>FTOpiePiece</code>
	 *
	 *
	 * @param <code>id</code> is of <code>String</code> type
	 * @param <code>name</code> is of <code>String</code> type
	 * @param <code>volume</code> is of <code>double</code> type
	 * @param <code>pl</code> is of <code>double</code> type
	 * @param <code>isProfit</code> is of <code>int</code> type
	 *
	 */
	public FTOpiePiece( String id, String name, double volume, double pl,
						int isProfit, Object source )
	{

		this.pieceName = name;
		this.volume    = volume;
		this.pl        = pl;
		this.isProfit  = isProfit;
		this.start     = start;
		this.finish    = finish;
		this.id        = id;
		this.source    = source;
	}

	protected Object getSource()
	{
		return source;
	}

	/**
	 * Method <code>getId</code>
	 *
	 *
	 * @return the value of <code>String</code> type
	 *
	 */
	public String getId()
	{
		return id;
	}

	/**
	 * Method <code>getGradientStart</code>
	 *
	 *
	 * @return the value of <code>Color</code> type
	 *
	 */
	public Color getGradientStart()
	{
		return gradStart;
	}

	/**
	 * Method <code>setGradientStart</code>
	 *
	 *
	 * @param <code>c</code> is of <code>Color</code> type
	 *
	 */
	public void setGradientStart( Color c )
	{
		gradStart = c;
	}

	/**
	 * Method <code>getName</code>
	 *
	 *
	 * @return the value of <code>String</code> type
	 *
	 */
	public String getName()
	{
		return pieceName;
	}

	/**
	 * Method <code>getVolume</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getVolume()
	{
		return volume;
	}

	/**
	 * Method <code>setVolume</code>
	 *
	 *
	 * @param <code>v</code> is of <code>double</code> type
	 *
	 */
	public void setVolume( double v )
	{
		volume = v;
	}

	/**
	 * Method <code>getPL</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getPL()
	{
		return pl;
	}

	/**
	 * Method <code>setPL</code>
	 *
	 *
	 * @param <code>pl</code> is of <code>double</code> type
	 *
	 */
	public void setPL( double pl )
	{
		this.pl = pl;
	}

	/**
	 * Method <code>getVolumeAngle</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getVolumeAngle()
	{
		return angleOfVolume;
	}

	/**
	 * Method <code>setVolumeAngle</code>
	 *
	 *
	 * @param <code>v</code> is of <code>double</code> type
	 *
	 */
	public void setVolumeAngle( double v )
	{
		angleOfVolume = v;
	}

	/**
	 * Method <code>getPLAngle</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getPLAngle()
	{
		return angleOfPL;
	}

	/**
	 * Method <code>setPLAngle</code>
	 *
	 *
	 * @param <code>pl</code> is of <code>double</code> type
	 *
	 */
	public void setPLAngle( double pl )
	{
		angleOfPL = pl;
	}

	/**
	 * Method <code>isProfitPiece</code>
	 *
	 *
	 * @return the value of <code>int</code> type
	 *
	 */
	public int isProfitPiece()
	{
		return isProfit;
	}

	/**
	 * Method <code>setProfit</code>
	 *
	 *
	 * @param <code>p</code> is of <code>int</code> type
	 *
	 */
	public void setProfit( int p )
	{
		isProfit = p;
	}

	/**
	 * Method <code>isPieceSelected</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean isPieceSelected()
	{
		return isSelected;
	}

	/**
	 * Method <code>setPieceSelected</code>
	 *
	 *
	 * @param <code>selected</code> is of <code>boolean</code> type
	 *
	 */
	public void setPieceSelected( boolean selected )
	{
		isSelected = selected;
	}

	/**
	 * Method <code>getPieceColor</code>
	 *
	 *
	 * @return the value of <code>Color</code> type
	 *
	 */
	public Color getPieceColor()
	{
		return pieceColor;
	}

	/**
	 * Method <code>setPieceColor</code>
	 *
	 *
	 * @param <code>color</code> is of <code>Color</code> type
	 *
	 */
	public void setPieceColor( Color color )
	{
		pieceColor = color;
	}

	/**
	 * Method <code>getGradient</code>
	 *
	 *
	 * @return the value of <code>GradientPaint</code> type
	 *
	 */
	public GradientPaint getGradient()
	{
		return gradient;
	}

	/**
	 * Method <code>setGradient</code>
	 *
	 *
	 * @param <code>gradient</code> is of <code>GradientPaint</code> type
	 *
	 */
	public void setGradient( GradientPaint gradient )
	{
		this.gradient = gradient;
	}

	/**
	 * Method <code>getStart</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getStart()
	{
		return start;
	}

	/**
	 * Method <code>setStart</code>
	 *
	 *
	 * @param <code>startAngle</code> is of <code>double</code> type
	 *
	 */
	public void setStart( double startAngle )
	{
		start = startAngle;
	}

	/**
	 * Method <code>getFinish</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getFinish()
	{
		return finish;
	}

	/**
	 * Method <code>setFinish</code>
	 *
	 *
	 * @param <code>finishAngle</code> is of <code>double</code> type
	 *
	 */
	public void setFinish( double finishAngle )
	{
		finish = finishAngle;
	}

	/**
	 * Method <code>getStartPL</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getStartPL()
	{
		return startPL;
	}

	/**
	 * Method <code>setStartPL</code>
	 *
	 *
	 * @param <code>startAngle</code> is of <code>double</code> type
	 *
	 */
	public void setStartPL( double startAngle )
	{
		startPL = startAngle;
	}

	/**
	 * Method <code>getFinishPL</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getFinishPL()
	{
		return finishPL;
	}

	/**
	 * Method <code>setFinishPL</code>
	 *
	 *
	 * @param <code>finishAngle</code> is of <code>double</code> type
	 *
	 */
	public void setFinishPL( double finishAngle )
	{
		finishPL = finishAngle;
	}

	/**
	 * Method <code>getShape</code>
	 *
	 *
	 * @return the value of <code>Shape</code> type
	 *
	 */
	public Shape getShape()
	{
		return instance;
	}

	/**
	 * Method <code>setShape</code>
	 *
	 *
	 * @param <code>shape</code> is of <code>Shape</code> type
	 *
	 */
	public void setShape( Shape shape )
	{
		instance = shape;
	}

	/**
	 * Method <code>getShapePL</code>
	 *
	 *
	 * @return the value of <code>Shape</code> type
	 *
	 */
	public Shape getShapePL()
	{
		return instancePL;
	}

	/**
	 * Method <code>setShapePL</code>
	 *
	 *
	 * @param <code>shape</code> is of <code>Shape</code> type
	 *
	 */
	public void setShapePL( Shape shape )
	{
		instancePL = shape;
	}

	/**
	 * Method <code>getShadow</code>
	 *
	 *
	 * @return the value of <code>Shape</code> type
	 *
	 */
	public Shape getShadow()
	{
		return shadow;
	}

	/**
	 * Method <code>setShadow</code>
	 *
	 *
	 * @param <code>shape</code> is of <code>Shape</code> type
	 *
	 */
	public void setShadow( Shape shape )
	{
		shadow = shape;
	}

	/**
	 * Method <code>isRepaintRequired</code>
	 *
	 *
	 * @return the value of <code>boolean</code> type
	 *
	 */
	public boolean isRepaintRequired()
	{
		return isRepaintNeeded;
	}

	/**
	 * Method <code>setRepaintRequired</code>
	 *
	 *
	 * @param <code>proceed</code> is of <code>boolean</code> type
	 *
	 */
	public void setRepaintRequired( boolean proceed )
	{
		isRepaintNeeded = proceed;
	}

	/**
	 * Method <code>toString</code>
	 *
	 *
	 * @return the value of <code>String</code> type
	 *
	 */
	public String toString()
	{

		String text = "\n";

		text += "**********************" + "\n";
		text += "**********************" + "\n";
		text += "ID = " + id + "\n";
		text += "Name = " + pieceName + "\n";
		text += "Volume = " + volume + "\n";
		text += "Pl = " + pl + "\n";
		text += "Profit = " + isProfit + "\n";
		text += "**********************" + "\n";

		return text;
	}
}

class FTOchartComparator implements Comparator
{

	/**
	 * Method <code>compare</code>
	 *
	 *
	 * @param <code>o1</code> is of <code>Object</code> type
	 * @param <code>o2</code> is of <code>Object</code> type
	 *
	 * @return the value of <code>int</code> type
	 *
	 */
	public int compare( Object o1, Object o2 )
	{

		int         result = 0;
		FTOpiePiece p1     = (FTOpiePiece) o1;
		FTOpiePiece p2     = (FTOpiePiece) o2;
		int         pr1    = p1.isProfitPiece();
		int         pr2    = p2.isProfitPiece();

		if ( pr1 != pr2 )
		{
			result = pr1 - pr2;
		}
		else
		{
			double a1 = p1.getPL();
			double a2 = p2.getPL();

			if ( pr1 == 0 )
			{
				a1 = p1.getVolume();
				a2 = p2.getVolume();
			}

			if ( pr1 < 0 )
				result = (int) ( a2 - a1 );
			else
				result = (int) ( a1 - a2 );
		}

		return result;
	}
}

class FTOchartPopupMenu extends JPopupMenu implements ActionListener
{

	private FTOchartFrame parent;
	private JMenuItem     showLegend     = null;
	private JMenuItem     showShadows    = null;
	private JMenuItem     gradientPaint  = null;
	private JMenuItem     ellipseEnabled = null;
	private JMenuItem     sortingEnabled = null;
	private JMenuItem     increaseGap    = null;
	private JMenuItem     decreaseGap    = null;

	/**
	 * Constructor <code>FTOchartPopupMenu</code>
	 *
	 *
	 * @param <code>parent</code> is of <code>FTOchartFrame</code> type
	 *
	 */
	public FTOchartPopupMenu( FTOchartFrame parent )
	{

		super();

		this.parent = parent;

		addMenuItems();
	}

	/**
	 * Method <code>destroy</code>
	 *
	 *
	 */
	public void destroy()
	{

		parent = null;

		removeAll();
	}

	/**
	 * Method <code>actionPerformed</code>
	 *
	 *
	 * @param <code>e</code> is of <code>ActionEvent</code> type
	 *
	 */
	public void actionPerformed( ActionEvent e )
	{

		JMenuItem source = (JMenuItem) e.getSource();

		if ( source == showLegend )
			parent.invertLegend();
		else if ( source == showShadows )
			parent.invertShadows();
		else if ( source == gradientPaint )
			parent.invertGradient();
		else if ( source == ellipseEnabled )
			parent.invertEllipse();
		else if ( source == sortingEnabled )
			parent.invertSorting();
		else if ( source == increaseGap )
			parent.increaseGap();
		else if ( source == decreaseGap )
			parent.decreaseGap();
	}

	private void addMenuItems()
	{

		showShadows = new JMenuItem( "" );

		showShadows.addActionListener( this );
		add( showShadows );
		showShadows.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_S, 0,
				false ) );

		ellipseEnabled = new JMenuItem( "" );

		ellipseEnabled.addActionListener( this );
		add( ellipseEnabled );
		ellipseEnabled.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_E,
				0, false ) );

		sortingEnabled = new JMenuItem( "" );

		sortingEnabled.addActionListener( this );

		/*add( sortingEnabled );
		sortingEnabled.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_O, 0,false ) );*/
		gradientPaint = new JMenuItem( "" );

		gradientPaint.addActionListener( this );
		add( gradientPaint );
		gradientPaint.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_G,
				0, false ) );

		JSeparator s = new JSeparator();

		add( s );

		increaseGap = new JMenuItem( "Increase gap" );

		increaseGap.addActionListener( this );
		add( increaseGap );
		increaseGap.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_UP,
				0, false ) );

		decreaseGap = new JMenuItem( "Decrease gap" );

		decreaseGap.addActionListener( this );
		add( decreaseGap );
		decreaseGap.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_DOWN,
				0, false ) );

		s = new JSeparator();

		add( s );

		showLegend = new JMenuItem( "" );

		showLegend.addActionListener( this );
		add( showLegend );
		showLegend.setAccelerator( KeyStroke.getKeyStroke( KeyEvent.VK_L,
				0 ) );

		//resetPopup();
	}

	/**
	 * Method <code>show</code>
	 *
	 *
	 * @param <code>invoker</code> is of <code>Component</code> type
	 * @param <code>x</code> is of <code>int</code> type
	 * @param <code>y</code> is of <code>int</code> type
	 *
	 */
	public void show( Component invoker, int x, int y )
	{

		if ( parent.isEmpty() )
			return;

		resetPopup();
		super.show( invoker, x, y );
	}

	private void resetPopup()
	{

		String text = new String();

		if ( parent.getShadows() )
			text = "Hide shadows";
		else
			text = "Show shadows";

		showShadows.setText( text );

		if ( parent.getEllipseEnabled() )
			text = "Disable ellipse";
		else
			text = "Enable ellipse";

		ellipseEnabled.setText( text );

		if ( parent.getLegend() )
			text = "Hide legend";
		else
			text = "Show legend";

		showLegend.setText( text );

		if ( parent.getSortingNeeded() )
			text = "Sorting disabled";
		else
			text = "Sorting enabled";

		sortingEnabled.setText( text );

		if ( parent.getGradientPaint() )
			text = "Use plain painting";
		else
			text = "Use Gradient painting";

		gradientPaint.setText( text );

		if ( (int) ( parent.getGap() * 1000 )
				== (int) ( FTO2dChart.MAX_GAP * 1000 ) )
			increaseGap.setEnabled( false );
		else
			increaseGap.setEnabled( true );

		if ( parent.getGap() == FTO2dChart.MIN_GAP )
			decreaseGap.setEnabled( false );
		else
			decreaseGap.setEnabled( true );
	}
}

//Test class
class FTOinputData
{

	private Object id;
	private String name;
	private double value;
	private double delta;

	/**
	 * Constructor <code>FTOinputData</code>
	 *
	 *
	 * @param <code>id</code> is of <code>Object</code> type
	 * @param <code>name</code> is of <code>String</code> type
	 * @param <code>value</code> is of <code>double</code> type
	 * @param <code>delta</code> is of <code>double</code> type
	 *
	 */
	public FTOinputData( Object id, String name, double value, double delta )
	{

		this.id    = id;
		this.name  = name;
		this.value = value;
		this.delta = delta;
	}

	/**
	 * Method <code>cloneMe</code>
	 *
	 *
	 * @return the value of <code>Object</code> type
	 *
	 */
	public Object cloneMe()
	{

		try
		{
			return super.clone();
		}
		catch ( java.lang.CloneNotSupportedException ex )
		{

			// do nothing - we're cloneable
			// this try/catch block is only to make the compiler happy
			return null;
		}
	}

	/**
	 * Method <code>getId</code>
	 *
	 *
	 * @return the value of <code>Object</code> type
	 *
	 */
	public Object getId()
	{
		return id;
	}

	/**
	 * Method <code>getSymbol</code>
	 *
	 *
	 * @return the value of <code>String</code> type
	 *
	 */
	public String getSymbol()
	{
		return name;
	}

	/**
	 * Method <code>getCurrentValue</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getCurrentValue()
	{
		return value;
	}

	/**
	 * Method <code>getDeltaValue</code>
	 *
	 *
	 * @return the value of <code>double</code> type
	 *
	 */
	public double getDeltaValue()
	{
		return delta;
	}

	/**
	 * Method <code>getDateTime</code>
	 *
	 *
	 * @return the value of <code>Date</code> type
	 *
	 */
	public Date getDateTime()
	{
		return new Date( System.currentTimeMillis() );
	}
}

class FTOplafMacros implements SwingConstants
{

	// don't make these final, since the value is
	// different on each platform
	private static String LINE_SEPARATOR     =
		System.getProperty( "line.separator" );
	private static int    LINE_SEPARATOR_LEN = LINE_SEPARATOR.length();

	/**
	 * Method <code>breakupLines</code>
	 *
	 *
	 * @param <code>text</code> is of <code>String</code> type
	 *
	 * @return the value of <code>String[]</code> type
	 *
	 */
	public static String[] breakupLines( String text )
	{

		int len = text.length();

		if ( len == 0 )
			return new String[]{ "" };
		else
		{
			Vector data  = new Vector( 10 );
			int    start = 0;
			int    i     = 0;

			while ( i < len )
			{
				if ( text.startsWith( LINE_SEPARATOR, i ) )
				{
					data.addElement( text.substring( start, i ) );

					start = i + LINE_SEPARATOR_LEN;
					i     = start;
				}
				else if ( text.charAt( i ) == '\n' )
				{
					data.addElement( text.substring( start, i ) );

					start = i + 1;
					i     = start;
				}
				else
				{
					i++;
				}
			}

			if ( start != len )
				data.addElement( text.substring( start ) );

			int    numlines = data.size();
			String lines[]  = new String[numlines];

			data.copyInto( lines );

			return lines;
		}
	}
}

class FTOmultiLineToolTipUI extends ToolTipUI
{

	/** Field <code>SINGLETON</code> */
	static FTOmultiLineToolTipUI SINGLETON = new FTOmultiLineToolTipUI();

	/** Field <code>DISPLAY_ACCELERATOR</code> */
	static boolean DISPLAY_ACCELERATOR = true;

	/** Field <code>accelerator_offset</code> */
	int accelerator_offset = 15;

	/** Field <code>inset</code> */
	int inset = 3;

	private FTOmultiLineToolTipUI() {}

	/**
	 * Method <code>initialize</code>
	 *
	 *
	 */
	public static void initialize()
	{

		// don't hardcode class name, this way we can obfuscate.
		String key  = "ToolTipUI";
		Class  cls  = SINGLETON.getClass();
		String name = cls.getName();

		UIManager.put( key, name );
		UIManager.put( name, cls );
	}

	/**
	 * Method <code>createUI</code>
	 *
	 *
	 * @param <code>c</code> is of <code>JComponent</code> type
	 *
	 * @return the value of <code>ComponentUI</code> type
	 *
	 */
	public static ComponentUI createUI( JComponent c )
	{
		return SINGLETON;
	}

	/**
	 * Method <code>installUI</code>
	 *
	 *
	 * @param <code>c</code> is of <code>JComponent</code> type
	 *
	 */
	public void installUI( JComponent c )
	{

		LookAndFeel.installColorsAndFont( c, "ToolTip.background",
										  "ToolTip.foreground",
										  "ToolTip.font" );
		LookAndFeel.installBorder( c, "ToolTip.border" );
	}

	/**
	 * Method <code>uninstallUI</code>
	 *
	 *
	 * @param <code>c</code> is of <code>JComponent</code> type
	 *
	 */
	public void uninstallUI( JComponent c )
	{
		LookAndFeel.uninstallBorder( c );
	}

	/**
	 * Method <code>setDisplayAcceleratorKey</code>
	 *
	 *
	 * @param <code>val</code> is of <code>boolean</code> type
	 *
	 */
	public static void setDisplayAcceleratorKey( boolean val )
	{
		DISPLAY_ACCELERATOR = val;
	}

	/**
	 * Method <code>getPreferredSize</code>
	 *
	 *
	 * @param <code>c</code> is of <code>JComponent</code> type
	 *
	 * @return the value of <code>Dimension</code> type
	 *
	 */
	public Dimension getPreferredSize( JComponent c )
	{

		Font font = c.getFont();

		//      FontMetrics fontMetrics =
		//          Toolkit.getDefaultToolkit().getFontMetrics( font );
		FontMetrics fontMetrics = c.getFontMetrics( font );

		//Toolkit.getDefaultToolkit().getFontMetrics( font );
		int    fontHeight = fontMetrics.getHeight();
		String tipText    = ( (JToolTip) c ).getTipText();

		if ( tipText == null )
			tipText = "";

		String    lines[]   = FTOplafMacros.breakupLines( tipText );
		int       num_lines = lines.length;
		Dimension dimension;
		int       width, height, onewidth;

		height = num_lines * fontHeight;
		width  = 0;

		for ( int i = 0; i < num_lines; i++ )
		{
			onewidth = fontMetrics.stringWidth( lines[i] );

			if ( DISPLAY_ACCELERATOR && ( i == num_lines - 1 ) )
			{
				String keyText = getAcceleratorString( (JToolTip) c );

				if ( !keyText.equals( "" ) )
					onewidth += fontMetrics.stringWidth( keyText )
								+ accelerator_offset;
			}

			width = Math.max( width, onewidth );
		}

		return new Dimension( width + inset * 2, height + inset * 2 );
	}

	/**
	 * Method <code>getMinimumSize</code>
	 *
	 *
	 * @param <code>c</code> is of <code>JComponent</code> type
	 *
	 * @return the value of <code>Dimension</code> type
	 *
	 */
	public Dimension getMinimumSize( JComponent c )
	{
		return getPreferredSize( c );
	}

	/**
	 * Method <code>getMaximumSize</code>
	 *
	 *
	 * @param <code>c</code> is of <code>JComponent</code> type
	 *
	 * @return the value of <code>Dimension</code> type
	 *
	 */
	public Dimension getMaximumSize( JComponent c )
	{
		return getPreferredSize( c );
	}

	/**
	 * Method <code>paint</code>
	 *
	 *
	 * @param <code>g</code> is of <code>Graphics</code> type
	 * @param <code>c</code> is of <code>JComponent</code> type
	 *
	 */
	public void paint( Graphics g, JComponent c )
	{

		Font font = c.getFont();

		//FontMetrics fontMetrics =
		//  Toolkit.getDefaultToolkit().getFontMetrics( font );
		FontMetrics fontMetrics = c.getFontMetrics( font );
		Dimension   dimension   = c.getSize();
		int         fontHeight  = fontMetrics.getHeight();
		int         fontAscent  = fontMetrics.getAscent();
		String      tipText     = ( (JToolTip) c ).getTipText();
		String      lines[]     = FTOplafMacros.breakupLines( tipText );
		int         num_lines   = lines.length;
		int         height;
		int         i;

		g.setColor( c.getBackground() );
		g.fillRect( 0, 0, dimension.width, dimension.height );
		g.setColor( c.getForeground() );

		for ( i = 0, height = 2 + fontAscent; i < num_lines;
				i++, height += fontHeight )
		{
			g.drawString( lines[i], inset, height );

			if ( DISPLAY_ACCELERATOR && ( i == num_lines - 1 ) )
			{
				String keyText = getAcceleratorString( (JToolTip) c );

				if ( !keyText.equals( "" ) )
				{
					Font smallFont = new Font( font.getName(),
											   font.getStyle(),
											   font.getSize() - 2 );

					g.setFont( smallFont );
					g.drawString( keyText,
								  fontMetrics.stringWidth( lines[i] )
								  + accelerator_offset, height );
				}
			}
		}
	}

	/**
	 * Method <code>getAcceleratorString</code>
	 *
	 *
	 * @param <code>tip</code> is of <code>JToolTip</code> type
	 *
	 * @return the value of <code>String</code> type
	 *
	 */
	public String getAcceleratorString( JToolTip tip )
	{

		JComponent comp = tip.getComponent();

		if ( comp == null )
			return "";

		KeyStroke[] keys          = comp.getRegisteredKeyStrokes();
		String      controlKeyStr = "";
		KeyStroke   postTip       = KeyStroke.getKeyStroke( KeyEvent.VK_F1,
										Event.CTRL_MASK );

		for ( int i = 0; i < keys.length; i++ )
		{

			// Ignore ToolTipManager postTip action,
			// in swing1.1beta3 and onward
			if ( postTip.equals( keys[i] ) )
				continue;

			char c   = (char) keys[i].getKeyCode();
			int  mod = keys[i].getModifiers();

			if ( mod == InputEvent.CTRL_MASK )
			{
				controlKeyStr = "Ctrl+" + (char) keys[i].getKeyCode();

				break;
			}
			else if ( mod == InputEvent.ALT_MASK )
			{
				controlKeyStr = "Alt+" + (char) keys[i].getKeyCode();

				break;
			}
		}

		return controlKeyStr;
	}
}

class FTOtoolTip extends JToolTip
{

	/**
	 * Method <code>updateUI</code>
	 *
	 *
	 */
	public void updateUI()
	{
		setUI( FTOmultiLineToolTipUI.SINGLETON );
	}
}
Comment 9 Philipe Mulet CLA 2002-02-11 07:18:02 EST
Was able to reproduce with test case, thanks much.
Comment 10 Philipe Mulet CLA 2002-02-11 07:49:44 EST
Simplified test case:

import javax.swing.JComponent;
import javax.swing.JPanel;

import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

public class FTOchartFrame {
	public void selectionChanged(int index, boolean notify) {}
}

class FTOchartLegend extends JPanel {

	FTOchartFrame parent;
	class FTOlegendPanel extends JComponent {
		public FTOlegendPanel() {
			Object x = new Object() {
				public void mousePressed(MouseEvent e) {
					parent.selectionChanged(0, true);
				}
			};
		}
	}
}

as soon as removing the super type (JComponent from FTOlegendPanel, it compiles 
ok)...


Comment 11 Philipe Mulet CLA 2002-02-11 07:51:47 EST
Dmitry,

As a work-around, you can qualify the invocation of parent.selectionChanged
(...) as follows, and it will compile ok.

FTOchartLegend.this.parent.selectionChanged(...)
Comment 12 Philipe Mulet CLA 2002-02-11 09:37:42 EST
Problem identified. When resolving single names, we did set the depth 
incorrectly (during each iteration of the loop through enclosing scopes) 
instead of when returning the found file only.

Fixed, regression test added (will go into next integration build).