/*
* @(#)BoundedRangeModel.java
*
* Copyright (c) 1999-2008 The authors and contributors of JHotDraw.
* You may not use, copy or modify this file, except in compliance with the
* accompanying license terms.
*/
//package ch.randelshofer.io;
package org.jhotdraw.io;
import javax.swing.event.*;
import javax.swing.BoundedRangeModel;
import java.io.*;
import edu.umd.cs.findbugs.annotations.NonNull;
import edu.umd.cs.findbugs.annotations.Nullable;
/**
* This input stream implements the BoundedRangeModel and allows
* the observation of the input reading process.
*
* @author Werner Randelshofer
* @version $Id$
* <br>history 1.0.1 02.05.1999 #setMaximum overrides the size information
* from the file.
* <br>history 1.0 20.03.1999 Derived from javax.swing.ProgressMonitorInputStream.
*/
public class BoundedRangeInputStream
extends FilterInputStream
implements BoundedRangeModel {
private int nread = 0;
private int size = 0;
private boolean valueIsAdjusting;
/**
* Only one ChangeEvent is needed per model instance since the
* event's only (read-only) state is the source property. The source
* of events generated here is always "this".
*/
@Nullable protected transient ChangeEvent changeEvent = null;
/** The listeners waiting for model changes. */
protected EventListenerList listenerList = new EventListenerList();
/**
* Create a new instance.
*/
public BoundedRangeInputStream(InputStream in) {
super(in);
try
{ size = in.available(); }
catch(IOException ioe)
{ size = 0; }
}
/**
* Overrides {@code FilterInputStream.read}
* to update the value after the read.
*/
@Override
public int read()
throws IOException {
int c = in.read();
if (c >=0)
{ incrementValue(1); }
return c;
}
/**
* Overrides {@code FilterInputStream.read}
* to update the value after the read.
*/
@Override
public int read(byte b[])
throws IOException {
int nr = in.read(b);
incrementValue(nr);
return nr;
}
/**
* Overrides {@code FilterInputStream.read}
* to update the value after the read.
*/
@Override
public int read(byte b[],int off,int len)
throws IOException {
int nr = in.read(b, off, len);
incrementValue(nr);
return nr;
}
/**
* Overrides {@code FilterInputStream.skip}
* to update the value after the skip.
*/
@Override
public long skip(long n) throws IOException {
long nr = in.skip(n);
incrementValue( (int)nr);
return nr;
}
/**
* Overrides {@code FilterInputStream.reset}
* to reset the progress monitor as well as the stream.
*/
@Override
public synchronized void reset()
throws IOException {
in.reset();
nread = size-in.available();
fireStateChanged();
}
/**
* Increments the extent by the indicated value.
* Negative Increments are ignored.
*
* @param inc The incrementValue value.
*/
private void incrementValue(int inc) {
if (inc > 0) {
nread+=inc;
if (nread > size)
{ size = nread; }
fireStateChanged();
}
}
/**
* Returns the minimum acceptable value.
*
* @return the value of the minimum property
* @see #setMinimum
*/
@Override
public int getMinimum()
{ return 0; }
/**
* Ignored: The minimum of an input stream is always zero.
*
* Sets the model's minimum to <I>newMinimum</I>. The
* other three properties may be changed as well, to ensure
* that:
* <pre>
* minimum <= value <= value+extent <= maximum
* </pre>
* <p>
* Notifies any listeners if the model changes.
*
* @param newMinimum the model's new minimum
* @see #getMinimum
* @see #addChangeListener
*/
@Override
public void setMinimum(int newMinimum) {}
/**
* Returns the model's maximum. Note that the upper
* limit on the model's value is (maximum - extent).
*
* @return the value of the maximum property.
* @see #setMaximum
* @see #setExtent
*/
@Override
public int getMaximum() { return size; }
/**
* Ignored: The maximum of an input stream can not be changed.
* #
* Sets the model's maximum to <I>newMaximum</I>. The other
* three properties may be changed as well, to ensure that
* <pre>
* minimum <= value <= value+extent <= maximum
* </pre>
* <p>
* Notifies any listeners if the model changes.
*
* @param newMaximum the model's new maximum
* @see #getMaximum
* @see #addChangeListener
*/
@Override
public void setMaximum(int newMaximum) {
size = newMaximum;
fireStateChanged();
}
/**
* Returns the current read position.
*
* Returns the model's current value. Note that the upper
* limit on the model's value is {@code maximum - extent}
* and the lower limit is {@code minimum}.
*
* @return the model's value
* @see #setValue
*/
@Override
public int getValue() { return nread; }
/**
* Ignored: The value is always zero.
*
* Sets the model's current value to {@code newValue} if {@code newValue}
* satisfies the model's constraints. Those constraints are:
* <pre>
* minimum <= value <= value+extent <= maximum
* </pre>
* Otherwise, if {@code newValue} is less than {@code minimum}
* it's set to {@code minimum}, if its greater than
* {@code maximum} then it's set to {@code maximum}, and
* if it's greater than {@code value+extent} then it's set to
* {@code value+extent}.
* <p>
* When a BoundedRange model is used with a scrollbar the value
* specifies the origin of the scrollbar knob (aka the "thumb" or
* "elevator"). The value usually represents the origin of the
* visible part of the object being scrolled.
* <p>
* Notifies any listeners if the model changes.
*
* @param newValue the model's new value
* @see #getValue
*/
@Override
public void setValue(int newValue) {}
/**
* This attribute indicates that any upcoming changes to the value
* of the model should be considered a single event. This attribute
* will be set to true at the start of a series of changes to the value,
* and will be set to false when the value has finished changing. Normally
* this allows a listener to only take action when the final value change in
* committed, instead of having to do updates for all intermediate values.
* <p>
* Sliders and scrollbars use this property when a drag is underway.
*
* @param b true if the upcoming changes to the value property are part of a series
*/
@Override
public void setValueIsAdjusting(boolean b)
{ valueIsAdjusting = b; }
/**
* Returns true if the current changes to the value property are part
* of a series of changes.
*
* @return the valueIsAdjustingProperty.
* @see #setValueIsAdjusting
*/
@Override
public boolean getValueIsAdjusting()
{ return valueIsAdjusting; }
/**
* Returns the model's extent, the length of the inner range that
* begins at the model's value.
*
* @return the value of the model's extent property
* @see #setExtent
* @see #setValue
*/
@Override
public int getExtent() { return 0; }
/**
* Ignored: The extent is always zero.
*/
@Override
public void setExtent(int newExtent)
{ }
/**
* Ignored: All values depend on the input stream.
*/
@Override
public void setRangeProperties(int value, int extent, int min, int max, boolean adjusting)
{}
/**
* Adds a ChangeListener to the model's listener list.
*
* @param l the ChangeListener to add
* @see #removeChangeListener
*/
@Override
public void addChangeListener(ChangeListener l) {
listenerList.add(ChangeListener.class, l);
}
/**
* Removes a ChangeListener.
*
* @param l the ChangeListener to remove
* @see #addChangeListener
* @see BoundedRangeModel#removeChangeListener
*/
@Override
public void removeChangeListener(ChangeListener l) {
listenerList.remove(ChangeListener.class, l);
}
/**
* Run each ChangeListeners stateChanged() method.
*
* @see #setRangeProperties
* @see EventListenerList
*/
protected void fireStateChanged() {
Object[] listeners = listenerList.getListenerList();
for (int i = listeners.length - 2; i >= 0; i -=2 ) {
if (listeners[i] == ChangeListener.class) {
if (changeEvent == null) {
changeEvent = new ChangeEvent(this);
}
((ChangeListener)listeners[i+1]).stateChanged(changeEvent);
}
}
}
}