/* * Trace2DLtd, a RingBuffer- based fast implementation of a ITrace2D. * Copyright (c) 2004 - 2011 Achim Westermann, Achim.Westermann@gmx.de * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA * * If you modify or optimize the code in a useful way please let me know. * Achim.Westermann@gmx.de */ package info.monitorenter.gui.chart.traces; import info.monitorenter.gui.chart.Chart2D; import info.monitorenter.gui.chart.ITrace2D; import info.monitorenter.gui.chart.ITracePoint2D; import info.monitorenter.util.collections.IRingBuffer; import info.monitorenter.util.collections.RingBufferArrayFast; import java.util.Iterator; /** * Additional to the Trace2DSimple the Trace2DLimited adds the following * functionality: * <p> * <ul> * <li>The amount of internal tracepoints is limited to the maxsize, passed to * the constructor.</li> * <li>If a new tracepoint is inserted and the maxsize has been reached, the * tracepoint residing for the longest time in this trace is thrown away.</li> * </UL> * Take this implementation to display frequently changing data (nonstatic, time * - dependant values). You will avoid a huge growing amount of tracepoints that * would increase the time for scaling and painting until system hangs or * java.lang.OutOfMemoryError is thrown. * <p> * * @author <a href='mailto:Achim.Westermann@gmx.de'>Achim Westermann </a> * * @version $Revision: 1.19 $ */ public class Trace2DLtd extends ATrace2D implements ITrace2D { /** Generated <code>serialVersionUID</code>. */ private static final long serialVersionUID = -6664475237146326176L; /** * Internal fast FIFO buffer implementation based upon indexed access to an * array. */ protected IRingBuffer<ITracePoint2D> m_buffer; /** * Constructs an instance with a default buffer size of 100. * <p> */ public Trace2DLtd() { this(100); } /** * Constructs an instance with a buffersize of maxsize and a default name. * <p> * * @param maxsize * the buffer size for the maximum amount of points that will be * shown. */ public Trace2DLtd(final int maxsize) { this(maxsize, Trace2DLtd.class.getName() + "-" + ATrace2D.getInstanceCount()); } /** * Constructs an instance with a buffersize of maxsize and a default name. * <p> * * @param maxsize * the buffer size for the maximum amount of points that will be * shown. * * @param name * the name that will be displayed for this trace. */ public Trace2DLtd(final int maxsize, final String name) { this.m_buffer = new RingBufferArrayFast<ITracePoint2D>(maxsize); this.setName(name); } /** * Creates an instance with a default buffersize of 100 and the given name. * <p> * * @param name * the name that will be displayed for the trace. */ public Trace2DLtd(final String name) { this(100, name); } /** * @see ATrace2D#addPointInternal(info.monitorenter.gui.chart.ITracePoint2D) */ @Override protected boolean addPointInternal(final ITracePoint2D p) { final ITracePoint2D removed = this.m_buffer.add(p); double tmpx; double tmpy; if (removed != null) { tmpx = removed.getX(); tmpy = removed.getY(); if (tmpx >= this.m_maxX) { tmpx = this.m_maxX; this.maxXSearch(); this.firePropertyChange(ITrace2D.PROPERTY_MAX_X, new Double(tmpx), new Double(this.m_maxX)); } else if (tmpx <= this.m_minX) { tmpx = this.m_minX; this.minXSearch(); this.firePropertyChange(ITrace2D.PROPERTY_MIN_X, new Double(tmpx), new Double(this.m_minX)); } if (tmpy >= this.m_maxY) { tmpy = this.m_maxY; this.maxYSearch(); this.firePropertyChange(ITrace2D.PROPERTY_MAX_Y, new Double(tmpy), new Double(this.m_maxY)); } else if (tmpy <= this.m_minY) { tmpy = this.m_minY; this.minYSearch(); this.firePropertyChange(ITrace2D.PROPERTY_MIN_Y, new Double(tmpy), new Double(this.m_minY)); } // scale the new point, check for new bounds! this.firePointAdded(p); // inform computing traces of removal: if (this.m_computingTraces.size() > 0) { for (final ITrace2D trace : this.m_computingTraces) { trace.removePoint(removed); } } } return true; } /** * @see java.lang.Object#equals(java.lang.Object) */ @Override public boolean equals(final Object obj) { if (this == obj) { return true; } if (!super.equals(obj)) { return false; } if (this.getClass() != obj.getClass()) { return false; } final Trace2DLtd other = (Trace2DLtd) obj; if (this.m_buffer == null) { if (other.m_buffer != null) { return false; } } else if (!this.m_buffer.equals(other.m_buffer)) { return false; } return true; } /** * @see info.monitorenter.gui.chart.ITrace2D#getMaxSize() */ public int getMaxSize() { return this.m_buffer.getBufferSize(); } /** * Returns the acutal amount of points in this trace. * <p> * * @return the acutal amount of points in this trace. * * @see info.monitorenter.gui.chart.ITrace2D#getSize() */ public int getSize() { return this.m_buffer.size(); } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { final int prime = 31; int result = super.hashCode(); result = prime * result + ((this.m_buffer == null) ? 0 : this.m_buffer.hashCode()); return result; } /** * @see info.monitorenter.gui.chart.ITrace2D#isEmpty() */ public boolean isEmpty() { return this.m_buffer.isEmpty(); } /** * @see info.monitorenter.gui.chart.ITrace2D#iterator() */ public Iterator<ITracePoint2D> iterator() { if (Chart2D.DEBUG_THREADING) { System.out.println("Trace2DLtd.iterator, 0 locks"); } this.ensureInitialized(); synchronized (this.m_renderer) { if (Chart2D.DEBUG_THREADING) { System.out.println("Trace2DLtd.iterator, 1 lock"); } synchronized (this) { if (Chart2D.DEBUG_THREADING) { System.out.println("Trace2DLtd.iterator, 2 locks"); } return this.m_buffer.iteratorL2F(); } } } /** * @see info.monitorenter.gui.chart.ITrace2D#removeAllPoints() */ @Override public void removeAllPointsInternal() { this.m_buffer.clear(); } /** * Returns null always because internally a ring buffer is used which does not * allow removing of values because that would break the contract of a ring * buffer. * <p> * * @param point * the point to remove. * * @return null always because internally a ring buffer is used which does not * allow removing of values because that would break the contract of a * ring buffer. * */ @Override protected ITracePoint2D removePointInternal(final ITracePoint2D point) { return null; } /** * Sets the maximum amount of points that may be displayed. * <p> * * Don't use this too often as decreases in size may cause expensive array * copy operations and new searches on all points for bound changes. * <p> * * TODO: Only search for bounds if size is smaller than before, debug and * test. * * @param amount * the new maximum amount of points to show. */ public final void setMaxSize(final int amount) { if (Chart2D.DEBUG_THREADING) { System.out.println("Trace2DLtd.setMaxSize, 0 locks"); } this.ensureInitialized(); synchronized (this.m_renderer) { if (Chart2D.DEBUG_THREADING) { System.out.println("Trace2DLtd.setMaxSize, 1 lock"); } synchronized (this) { if (Chart2D.DEBUG_THREADING) { System.out.println("Trace2DLtd.setMaxSize, 2 locks"); } this.m_buffer.setBufferSize(amount); final double xmin = this.m_minX; this.minXSearch(); if (this.m_minX != xmin) { this.firePropertyChange(ITrace2D.PROPERTY_MIN_X, new Double(xmin), new Double(this.m_minX)); } final double xmax = this.m_maxX; this.maxXSearch(); if (this.m_maxX != xmax) { this.firePropertyChange(ITrace2D.PROPERTY_MAX_X, new Double(xmax), new Double(this.m_maxX)); } final double ymax = this.m_maxY; this.maxYSearch(); if (this.m_maxY != ymax) { this.firePropertyChange(ITrace2D.PROPERTY_MAX_Y, new Double(ymax), new Double(this.m_maxY)); } final double ymin = this.m_minY; this.minYSearch(); if (this.m_minY != ymin) { this.firePropertyChange(ITrace2D.PROPERTY_MIN_Y, new Double(ymin), new Double(this.m_minY)); } } } } }