Transient

How to use...

Page 1 of 1

3 Replies - 789 Views - Last Post: 14 May 2010 - 10:01 PM Rate Topic: -----

#1 hawkysu  Icon User is offline

  • I am a spoon
  • member icon

Reputation: 5
  • View blog
  • Posts: 1,432
  • Joined: 20-February 09

Transient

Posted 14 May 2010 - 10:10 AM

I'm having trouble using the transient keyword (mainly because I don't know how) in my program. I'm supposed to make the canvas in my Shape class transient, but I also need the canvas to do repaint methods. When I try to save and load a file, I get a Null Pointer Exception at
(in Shape)
    protected void updateCanvas(Rectangle areaOfChange, boolean enlargeForKnobs){
	Rectangle toRedraw = new Rectangle(areaOfChange);
	if (enlargeForKnobs)
	    toRedraw.grow(KNOB_SIZE/2, KNOB_SIZE/2);
	canvas.repaint(toRedraw);//<--
    }


And when I try to move it, I get one at:
(in DrawingCanvas)
	public void mouseDragged(MouseEvent event){
	    Point curPt = event.getPoint();
	    switch (dragStatus) {
	    case DRAG_MOVE:
		selectedShape.translate(curPt.x - dragAnchor.x, //<--
					curPt.y - dragAnchor.y);//<--
		// update for next dragged event 
		dragAnchor = curPt; 
		break;
	    case DRAG_CREATE: case DRAG_RESIZE:
		 selectedShape.resize(dragAnchor, curPt);
		break;
	    }
	    repaint();
	}
	


How would I make it so I can call these methods without a problem?
------
Shape.java
import java.awt.*;
import java.io.Serializable;
import java.awt.datatransfer.*;

public abstract class Shape implements Serializable, Cloneable{
	private Toolbar tb= new Toolbar();
	protected Color shapecolor;
	protected Rectangle bounds;
    public transient DrawingCanvas canvas;
    protected boolean isSelected; 
    protected static final int KNOB_SIZE = 6;
    protected static final int NONE = -1;
    protected static final int NW = 0;
    protected static final int SW = 1;
    protected static final int SE = 2;
    protected static final int NE = 3;
    
	public Shape(Point start, DrawingCanvas dcanvas){
		canvas=dcanvas;
		bounds= new Rectangle(start);
		setColor(getColor());
	}
	
	public void setColor(Color setcol){
		 shapecolor=setcol;
	}
	
	public Color getColor(){
		return canvas.toolbar.getCurrentColor();
	}
	 /** The "primitive" for all resizing/moving/creating operations that
     * affect the rect bounding box. The current implementation just resets
     * the bounds variable and triggers a re-draw of the union of the old &
     * new rectangles. This will redraw the shape in new size and place and
     * also "erase" if bounds are now smaller than before.  It is a good 
     * design to have all changes to a critical variable bottleneck through
     * one method so that you can be sure that all the updating that goes 
     * with it only needs to be implemented in this one place. If any of your 
     * subclasses have additional work to do when the bounds change, this is 
     * the method to override.  Make sure that any methods that change the 
     * bounds call this method instead of directly manipulating the variable.
     */
    
    protected void setBounds(Rectangle newBounds){
	Rectangle oldBounds = bounds; 
	bounds = newBounds;
	updateCanvas(oldBounds.union(bounds));
    }
    
    /** The resize operation is called when first creating a rect, as well as
     * when later resizing by dragging one of its knobs.  The two parameters
     * are the points that define the new bounding box.  The anchor point
     * is the location of the mouse-down event during a creation operation
     * or the opposite corner of the knob being dragged during a resize 
     * operation. The end is the current location of the mouse.  If you 
     * create the smallest rectangle which encloses these two points, you 
     * will have the new bounding box.  Use the setBounds() primitive which 
     * is the bottleneck we are using for all geometry changes, it handles 
     * updating and redrawing.
     */
    public void resize(Point anchor, Point end){
	Rectangle newRect = new Rectangle(anchor);
	// creates smallest rectangle which 
	// includes both anchor & end
	newRect.add(end); 
	// reset bounds & redraw affected areas
	setBounds(newRect);  	
    }
    
    /** The translate operation is called when moving a shape by dragging in 
     * the canvas.  The two parameters are the delta-x and delta-y to move 
     * by.  Note that either or both can be negative. Create a new rectangle
     * from our bounds and translate and then go through the setBounds() 
     * primitive to change it.
     */
    
    public void translate(int dx, int dy){
	Rectangle newRect = new Rectangle(bounds);
	newRect.translate(dx, dy);
	setBounds(newRect);
    }

    /** Used to change the selected state of the shape which will require 
     * updating the affected area of the canvas to add/remove knobs.
     */
    
    public void setSelected(boolean newState){
	isSelected = newState; 
	// need to erase/add knobs 
	// including extent of extended bounds
	updateCanvas(bounds, true); 
    }
    /** The updateCanvas() methods are used when the state has changed
     * in such a way that it needs to be refreshed in the canvas to properly 
     * reflect the new settings. The shape should take responsibility for 
     * messaging the canvas to properly update itself. The appropriate AWT/JFC 
     * way to re-draw a component is to send it the repaint()  method with the
     * rectangle that needs refreshing.  This will cause an update()  event to
     * be sent to the component which in turn will call paint(), where the
     * real drawing implementation goes.  See the paint() method in 
     * DrawingCanvas to see how it is implemented.
     */
    
    protected void updateCanvas(Rectangle areaOfChange, boolean enlargeForKnobs){
	Rectangle toRedraw = new Rectangle(areaOfChange);
	if (enlargeForKnobs)
	    toRedraw.grow(KNOB_SIZE/2, KNOB_SIZE/2);
	canvas.repaint(toRedraw);
    }
    
    protected void updateCanvas(Rectangle areaOfChange){
	updateCanvas(areaOfChange, isSelected);
    }
    /** When needed, we create the array of knob rectangles on demand. This
     * does mean we create and discard the array and rectangles repeatedly. 
     * These are small objects, so perhaps it is not a big deal, but
     * a valid alternative would be to store the array of knobs as an 
     * instance variable of the Shape and and update the knobs as the bounds 
     * change. This means a little more memory overhead for each Shape
     * (since it is always storing the knobs, even when not being used) and 
     * having that redundant data opens up the possibility of bugs from 
     * getting out of synch (bounds move but knobs didn't, etc.) but you may 
     * find that a more appealing way to go.  Either way is fine with us. 
     * Note this method provides a nice unified place for one override from 
     * a shape subclass to substitute fewer or different knobs.
     */
    
    protected Rectangle[] getKnobRects(){
	Rectangle[] knobs = new Rectangle[4];
	knobs[NW] = new Rectangle(bounds.x - KNOB_SIZE/2, 
				  bounds.y - KNOB_SIZE/2, KNOB_SIZE, KNOB_SIZE);
	knobs[SW] = new Rectangle(bounds.x - KNOB_SIZE/2, 
				  bounds.y + bounds.height - KNOB_SIZE/2, 
				  KNOB_SIZE, KNOB_SIZE);
	knobs[SE] = new Rectangle(bounds.x + bounds.width - KNOB_SIZE/2, 
				  bounds.y + bounds.height - KNOB_SIZE/2, 
				  KNOB_SIZE, KNOB_SIZE);
	knobs[NE] = new Rectangle(bounds.x + bounds.width - KNOB_SIZE/2, 
				  bounds.y - KNOB_SIZE/2, 
				  KNOB_SIZE, KNOB_SIZE);
	return knobs;
    }
    
    /** Helper method to determine if a point is within one of the resize 
     * corner knobs.  If not selected, we have no resize knobs, so it can't 
     * have been a click on one.  Otherwise, we calculate the knob rects and 
     * then check whether the point falls in one of them.  The return value 
     * is one of NW, NE, SW, SE constants depending on which knob is found,
     * or NONE if the click doesn't fall within any knob. 
     */

    protected int getKnobContainingPoint(Point pt){
	// if we aren't selected, the knobs 
	// aren't showing and thus there are no knobs to check
	if (!isSelected) return NONE;
	Rectangle[] knobs = getKnobRects();
	for (int i = 0; i < knobs.length; i++)
	    if (knobs[i].contains(pt)) 
		return i;
	return NONE;
    }
    
    /** Method used by DrawingCanvas to determine if a mouse click is starting 
     * a resize event. In order for it to be a resize, the click must have 
     * been within one of the knob rects (checked by the helper method 
     * getKnobContainingPoint) and if so, we return the "anchor" ie the knob 
     * opposite this corner that will remain fixed as the user drags the 
     * resizing knob of the other corner around. During the drag actions of a 
     * resize, that fixed anchor point and the current mouse point will be 
     * passed to the resize method, which will reset the bounds in response
     * to the movement. If the mouseLocation wasn't a click in a knob and 
     * thus not the beginning of a resize event, null is returned.
     */
    
    public Point getAnchorForResize(Point mouseLocation)
    {
     int whichKnob = getKnobContainingPoint(mouseLocation);
     if (whichKnob == NONE) // no resize knob is at this location
         return null;
     Rectangle[] knobs = getKnobRects();
     whichKnob = Math.abs(whichKnob - (int)(knobs.length / 2));
     return (new Point(knobs[whichKnob].x + knobs[whichKnob].width /2,
                 knobs[whichKnob].y + knobs[whichKnob].height/2));
    }	
	public abstract void draw(Graphics g, Rectangle regionToDraw);
	public abstract boolean inside(Point pt);
	
	public Object Clone(){
		try {
			return this.clone();
		} catch (CloneNotSupportedException e) {
			return null;
		}
		
	}
}


DrawingCanvas.java
/* 
 *-------------------------------------------------------------- 80 columns ---|
 * The DrawingCanvas class a small extension of JComponent
 * @version      1.1 15/04/01
 * @author    Julie Zelenski
 * @author    (touched up by Ian A. Mason)
 * @see       javax.swing.JComponent
 */

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.event.*;

import java.util.*;

public class DrawingCanvas extends JComponent implements ChangeListener{
    static final int DRAG_NONE = 0;
    static final int DRAG_CREATE = 1; 
    static final int DRAG_RESIZE = 2; 
    static final int DRAG_MOVE = 3;

    // list of all shapes on canvas
    protected Vector allShapes;		
    // currently selected shape (can be null at times)
    protected Shape selectedShape;       
    // reference to toolbar to message for tool&color settings
    protected Toolbar toolbar;
    protected HMClip clip=new HMClip();

    
    /* These are the unimplemented menu commands.  The menus are already
     * set up to send the correct messages to the canvas, but the
     * method bodies themselves are currently completely empty. It will
     * be your job to fill them in!
     */
    public void cut() {
    	if(selectedShape!=null){
    		clip.setClip(selectedShape);
    		allShapes.remove(selectedShape);
    	}
    	else System.err.println("Nothing Selected");
    	repaint();
    	
    }
    public void copy() {
    	if(selectedShape!=null){
    		clip.setClip(selectedShape);
    	}
    	else System.err.println("Nothing Selected");
    	repaint();
    }
    public void paste() {
    	if(clip.clip!=null){
    		selectedShape= (Shape) clip.getClip();
    		allShapes.add(selectedShape);
  
    			selectedShape.draw(getGraphics(), getBounds());
    		
    	}
    	repaint();
    }
    public void delete() {
    	if(selectedShape!=null){
    		allShapes.remove(selectedShape);
    	}
    	repaint();
    }   
    public void clearAll() {
    	allShapes.removeAllElements();
    	repaint();
    }   
    public void loadFile() {
    	selectedShape=null;
    	String fname=filenameChosenByUser(true);
    	if(fname==null) fname="No File Name";
    	SimpleObjectReader read= SimpleObjectReader.openFileForReading(fname);
    	Shape incoming;
    	if (read == null) {
    		         System.out.println("Couldn't open file!");
    		         return;
    		     }
    		clearAll();
    		while((incoming = (Shape) read.readObject())!=null){
    			allShapes.add(incoming);
    		}
        	read.close();
        	repaint();
    }
    public void saveToFile() {
    	selectedShape=null;
    	String fname=filenameChosenByUser(false);
    	if(fname==null) fname="No File Name";
    	SimpleObjectWriter write= SimpleObjectWriter.openFileForWriting(fname);
    	if (allShapes==null) return;
    	if (write!=null){
    	if(allShapes!=null){
    		for(int i=0; i<allShapes.size();i++)
    		write.writeObject(allShapes.get(i));
    	}
    	}
    	else System.err.println("Couldn't Open file");
    	write.close();
    	repaint();
    }
    public void bringToFront() {
    	if(selectedShape!=null){
    	allShapes.add(allShapes.lastIndexOf(allShapes.lastElement()), selectedShape);
    	allShapes.remove(selectedShape);
    	}
    	repaint();
    }
    
    public void sendToBack() {
    	if(selectedShape!=null){
    		allShapes.remove(selectedShape);
    		allShapes.add(0, selectedShape);
    		
    	}
    		
    	repaint();
    }
    
    /**
     * Constructor for creating a new empty DrawingCanvas. We set up
     * our size and background colors, instantiate an empty vector of shapes,
     * and install a listener for mouse events using our inner class
     * CanvasMouseHandler
     */
    
    public DrawingCanvas(Toolbar tb, int width, int height){
	setPreferredSize(new Dimension(width, height));
	setBackground(Color.white);
	toolbar = tb;
	allShapes = new Vector();
	selectedShape = null;
	CanvasMouseHandler handler = new CanvasMouseHandler();
	addMouseListener(handler);
	addMouseMotionListener(handler);
	toolbar.addChangeListener(this);
    }
    
    /**
     * All components are responsible for drawing themselves in
     * response to repaint() requests.  The standard method a component
     * overrides is paint(Graphics g), but for Swing components, the default
     * paint() handler calls paintBorder(), paintComponent() and paintChildren()
     * For a Swing component, you override paintComponent and do your
     * drawing in that method.  For the drawing canvas, we want to
     * clear the background, then iterate through our shapes asking each
     * to draw. The Graphics object is clipped to the region to update
     * and we use to that avoid needlessly redrawing shapes outside the
     * update region.
     */
    
    public void paintComponent(Graphics g){
	Rectangle regionToRedraw = g.getClipBounds();
	g.setColor(getBackground());
	g.fillRect(regionToRedraw.x, regionToRedraw.y, 
		   regionToRedraw.width, regionToRedraw.height);
	Iterator iter = allShapes.iterator();
	while (iter.hasNext()){;
		((Shape)iter.next()).draw(g, regionToRedraw);
	}
    }
    
    /**
     * Changes the currently selected shape. There is at most
     * one shape selected at a time on the canvas. It is possible
     * for the selected shape to be null. Messages the shape to
     * change its selected state which will in turn refresh the
     * shape with the knobs active.
     */
    
    protected void setSelectedShape(Shape shapeToSelect) {
	// if change in selection
	if (selectedShape != shapeToSelect) { 
	    // deselect previous selection
	    if (selectedShape != null) 
		 selectedShape.setSelected(false);
	    // set selection to new shape
	    selectedShape = shapeToSelect;
	    if (selectedShape != null) {
		shapeToSelect.setSelected(true);
		toolbar.colorButton.setBackground(selectedShape.shapecolor);
	    }
	    
	}
    }
    
    /**
     * A hit-test routine which finds the topmost shape underneath a
     * given point.We search Vector of shapes in back-to-front order
     * since shapes created later are added to end and drawn last, thus 
     * appearing to be "on top" of the earlier ones.  When a click comes 
     * in, we want to select the top-most shape.
     */
    
    protected Shape shapeContainingPoint(Point pt){
	for (int i = allShapes.size()-1; i >= 0; i--) {
	    Shape r = (Shape)allShapes.elementAt(i);
	    if (r.inside(pt)) return r;
	}
	return null;
    }
    
    /**
     * The inner class CanvasMouseHandler is the object that handles the 
     * mouse actions (press, drag, release) over the canvas. Since there is 
     * a bit of state to drag during the various operations (which shape, 
     * where we started from, etc.) it is convenient to encapsulate all that 
     * state with this little convenience object and register it as the 
     * handler for mouse events on the canvas.
     */
    
    protected class CanvasMouseHandler 
	extends MouseAdapter implements MouseMotionListener {
	Point dragAnchor;		
	// variables using to track state during drag operations
	int dragStatus;
	/** When the mouse is pressed we need to figure out what
	 * action to take.  If the tool mode is arrow, the click might
	 * be a select, move or reisze. If the tool mode is one of the
	 * shapes, the click initiates creation of a new shape.
	 */
	public void mousePressed(MouseEvent event){
	    Shape clicked = null;
	    Point curPt = event.getPoint();
	    // first, determine if click was on resize knob of selected shape
	    if (toolbar.getCurrentTool() == Toolbar.SELECT) {
		if (selectedShape != null && 
		    (dragAnchor = selectedShape.getAnchorForResize(curPt)) 
		    != null) {
		    // drag will resize this shape
		    dragStatus = DRAG_RESIZE;	
		} else if ((clicked = shapeContainingPoint(curPt)) != null) { 
		    // if not, check if any shape was clicked
		    setSelectedShape(clicked);
		    // drag will move this shape  	
		    dragStatus = DRAG_MOVE;   
		    dragAnchor = curPt;
		} else {	
		    // else this was a click in empty area, 
		    // deselect selected shape,
		    setSelectedShape(null);
		    // drag does nothing in this case
		    dragStatus = DRAG_NONE;   
		}
	    } else {
	    	Shape newShape;
	    	switch(toolbar.getCurrentTool()){
	    	case Toolbar.RECT:
	    	    // create rect here
	    	    newShape = new Rect(curPt,DrawingCanvas.this);   
	    	    break;
	    	case Toolbar.OVAL:
	    	    // create oval here
	    		 newShape = new Oval(curPt,DrawingCanvas.this);   
		    	    break;
	    	case Toolbar.LINE:
	    	    // create line here
	    		 newShape = new Lines(curPt,DrawingCanvas.this);   
		    	    break;
	    	case Toolbar.SCRIBBLE: 
	    		 newShape = new Scribbles(curPt,DrawingCanvas.this);   
		    	    break;
	    	default: 
	    	    newShape = null;
	    	    break;
	    	}   
		// create rect here
		allShapes.add(newShape);
		setSelectedShape(newShape);
		dragStatus = DRAG_CREATE;		
		// drag will create (resize) this shape 
		dragAnchor = curPt;
	    }
	    repaint();
	}
	
	/** As the mouse is dragged, our listener will receive periodic
	 * updates as mouseDragged events. When we get an update position,
	 * we update the move/resize event that is in progress.
	 */
	public void mouseDragged(MouseEvent event){
	    Point curPt = event.getPoint();
	    switch (dragStatus) {
	    case DRAG_MOVE:
		selectedShape.translate(curPt.x - dragAnchor.x, 
					curPt.y - dragAnchor.y);
		// update for next dragged event 
		dragAnchor = curPt; 
		break;
	    case DRAG_CREATE: case DRAG_RESIZE:
		 selectedShape.resize(dragAnchor, curPt);
		break;
	    }
	    repaint();
	}
	
	public void mouseMoved(MouseEvent e) {}
	
    }
    
    
    /** A little helper routine that will be useful for the load & save
     * operations.  It brings up the standard JFileChooser dialog and
     * allows the user to specify a file to open or save. The return
     * value is the full path to the chosen file or null if no file was
     * selected.
     */
    protected String filenameChosenByUser(boolean forOpen){
	JFileChooser fc = new JFileChooser(System.getProperty("user.dir") + 
					   java.io.File.separator + "Documents");
	int result = (forOpen? (fc.showOpenDialog(this)) : 
		      fc.showSaveDialog(this));
	java.io.File chosenFile = fc.getSelectedFile();
	if (result == JFileChooser.APPROVE_OPTION && chosenFile != null)
	    return chosenFile.getPath();
	return null; 
	// return null if no file chosen or dialog cancelled
    }
	@Override
	public void stateChanged(ChangeEvent colorchange) {
		if(selectedShape!=null){
			selectedShape.setColor(toolbar.getCurrentColor());
		}
		repaint();
	}
    
    
}



Is This A Good Question/Topic? 0
  • +

Replies To: Transient

#2 g00se  Icon User is offline

  • D.I.C Lover
  • member icon

Reputation: 2553
  • View blog
  • Posts: 10,640
  • Joined: 20-September 08

Re: Transient

Posted 14 May 2010 - 12:00 PM

You need to ensure that any methods referencing the canvas have a DrawingCanvas instance, as one will not be provided by default on deserialization, and that will be the case whether or not it's transient
Was This Post Helpful? 2
  • +
  • -

#3 hawkysu  Icon User is offline

  • I am a spoon
  • member icon

Reputation: 5
  • View blog
  • Posts: 1,432
  • Joined: 20-February 09

Re: Transient

Posted 14 May 2010 - 12:50 PM

Ah, thank you. That makes sense!
Was This Post Helpful? 0
  • +
  • -

#4 pbl  Icon User is offline

  • There is nothing you can't do with a JTable
  • member icon

Reputation: 8315
  • View blog
  • Posts: 31,836
  • Joined: 06-March 08

Re: Transient

Posted 14 May 2010 - 10:01 PM

View Posthawkysu, on 14 May 2010 - 11:10 AM, said:

I'm having trouble using the transient keyword (mainly because I don't know how) in my program. I'm supposed to make the canvas in my Shape class transient, but I also need the canvas to do repaint methods. When I try to save and load a file, I get a Null Pointer Exception at
(in Shape)

transient means do not save in file when asked to do so... so normal it is null when you read it back

This post has been edited by pbl: 16 May 2010 - 03:53 PM
Reason for edit:: fixed [][]

Was This Post Helpful? 2
  • +
  • -

Page 1 of 1