1 /* 2 * $RCSfile: RemoteRenderedOp.java,v $ 3 * 4 * Copyright (c) 2005 Sun Microsystems, Inc. All rights reserved. 5 * 6 * Use is subject to license terms. 7 * 8 * $Revision: 1.2 $ 9 * $Date: 2006/06/16 22:52:05 $ 10 * $State: Exp $ 11 */package com.lightcrafts.mediax.jai.remote; 12 13 import java.awt.Point; 14 import java.awt.Rectangle; 15 import java.awt.RenderingHints; 16 import java.awt.Shape; 17 import java.awt.geom.Area; 18 import java.awt.geom.GeneralPath; 19 import java.awt.image.Raster; 20 import java.awt.image.RenderedImage; 21 import java.awt.image.renderable.ParameterBlock; 22 import java.beans.PropertyChangeEvent; 23 import java.beans.PropertyChangeListener; 24 import java.util.ArrayList; 25 import java.util.Collection; 26 import java.util.HashSet; 27 import java.util.Locale; 28 import java.util.Set; 29 import java.util.Vector; 30 import java.text.MessageFormat; 31 import com.lightcrafts.mediax.jai.CollectionChangeEvent; 32 import com.lightcrafts.mediax.jai.CollectionOp; 33 import com.lightcrafts.mediax.jai.JAI; 34 import com.lightcrafts.mediax.jai.OperationRegistry; 35 import com.lightcrafts.mediax.jai.PlanarImage; 36 import com.lightcrafts.mediax.jai.PropertyChangeEventJAI; 37 import com.lightcrafts.mediax.jai.PropertySourceChangeEvent; 38 import com.lightcrafts.mediax.jai.RegistryMode; 39 import com.lightcrafts.mediax.jai.RenderedOp; 40 import com.lightcrafts.mediax.jai.RenderingChangeEvent; 41 import com.lightcrafts.mediax.jai.TileCache; 42 import com.lightcrafts.mediax.jai.registry.RemoteRIFRegistry; 43 import com.lightcrafts.mediax.jai.util.ImagingException; 44 import com.lightcrafts.mediax.jai.util.ImagingListener; 45 import com.lightcrafts.media.jai.util.ImageUtil; 46 47 /** 48 * A node in a remote rendered imaging chain. This class is a concrete 49 * implementation of the <code>RemoteRenderedImage</code> interface. A 50 * <code>RemoteRenderedOp</code> stores a protocol name (as a 51 * <code>String</code>), a server name (as a <code>String</code>), an 52 * operation name (as a <code>String</code>), a 53 * <code>ParameterBlock</code> containing sources and miscellaneous 54 * parameters, and a <code>RenderingHints</code> containing rendering 55 * hints. A set of nodes may be joined together via the source 56 * <code>Vector</code>s within their <code>ParameterBlock</code>s to 57 * form a <u>d</u>irected <u>a</u>cyclic <u>g</u>raph (DAG). The topology 58 * i.e., connectivity of the graph may be altered by changing the 59 * <code>ParameterBlock</code>s; the operation name, parameters, and 60 * rendering hints may also be changed. 61 * 62 * <p> Such chains represent and handle operations that are being 63 * performed remotely. They convey the structure of an imaging 64 * chain in a compact representation and can be used to influence the 65 * remote imaging process (through the use of retry interval, retries and 66 * negotiation preferences). 67 * 68 * <p> <code>RemoteRenderedOp</code>s are a client side representation of 69 * the chain of operations taking place on the server. 70 * 71 * <p> The translation between <code>RemoteRenderedOp</code> chains and 72 * <code>RemoteRenderedImage</code> (usually 73 * <code>PlanarImageServerProxy</code>) chains makes use of two levels of 74 * indirection provided by the <code>OperationRegistry</code> and 75 * <code>RemoteRIF</code> facilities. First, the 76 * local <code>OperationRegistry</code> is used to map the protocol 77 * name into a <code>RemoteRIF</code>. This <code>RemoteRIF</code> then 78 * constructs one or more <code>RemoteRenderedImage</code>s (usually 79 * <code>PlanarImageServerProxy</code>s) to do the actual work (or 80 * returns a <code>RemoteRenderedImage</code> by other means. The 81 * <code>OperationRegistry</code> maps a protocol name into a 82 * <code>RemoteRIF</code>, since there is one to one correspondence 83 * between a protocol name and a <code>RemoteRIF</code>. This differs from 84 * the case of <code>RenderedOp</code>s, where the 85 * <code>OperationRegistry</code> maps each operation name to a 86 * <code>RenderedImageFactory</code> (RIF), since there is a one to one 87 * correspondence between an operation name and a RIF. The 88 * <code>RemoteRIF</code>s are therefore protocol-specific and not operation 89 * specific, while a RIF is operation specific. 90 * 91 * <p> Once a protocol name has been mapped into a <code>RemoteRIF</code>, 92 * the <code>RemoteRIF.create()</code> method is used to create a rendering. 93 * This rendering is responsible for communicating with the server to 94 * perform the specified operation remotely. 95 * 96 * <p> By virtue of being a subclass of <code>RenderedOp</code>, this class 97 * participates in Java Bean-style events as specified by 98 * <code>RenderedOp</code>. This means that <code>PropertyChangeEmitter</code> 99 * methods may be used to register and unregister 100 * <code>PropertyChangeListener</code>s. <code>RemoteRenderedOp</code>s 101 * are also <code>PropertyChangeListener</code>s so that they may be 102 * registered as listeners of other <code>PropertyChangeEmitter</code>s 103 * or the equivalent. Each <code>RemoteRenderedOp</code> also automatically 104 * receives any <code>RenderingChangeEvent</code>s emitted by any of its 105 * sources which are <code>RenderedOp</code>s. 106 * 107 * <p> <code>RemoteRenderedOp</code>s add the server name and the protocol 108 * name to the critical attributes, the editing (changing) of which, 109 * coupled with a difference in the old and new rendering over some 110 * non-empty region, may cause a <code>RenderingChangeEvent</code> to 111 * be emitted. As with <code>RenderedOp</code>, editing of a critical 112 * attribute of a <code>RemoteRenderedOp</code> will cause a 113 * <code>PropertyChangeEventJAI</code> detailing the change to be fired 114 * to all registered <code>PropertyChangeListener</code>s. 115 * <code>RemoteRenderedOp</code> registers itself as a 116 * <code>PropertyChangeListener</code> for all critical attributes, and 117 * thus receives all <code>PropertyChangeEventJAI</code> events generated 118 * by itself. This is done in order to allow the event handling code 119 * to generate a new rendering and reuse any tiles that might be valid 120 * after the critical argument change. 121 * 122 * <p> When a <code>RemoteRenderedOp</code> node receives a 123 * <code>PropertyChangeEventJAI</code> from itself, the region of 124 * the current rendering which is invalidated is computed using 125 * <code>RemoteDescriptor.getInvalidRegion()</code>. When a 126 * <code>RemoteRenderedOp</code> node receives a 127 * <code>RenderingChangeEvent</code> from one of its sources, the region of 128 * the current rendering which is invalidated is computed using 129 * the <code>mapSourceRect()</code> method of the current rendering and 130 * the invalid region of the source (retrieved using 131 * <code>RenderingChangeEvent.getInvalidRegion()</code>) 132 * If the complement of the invalid region contains any tiles of the 133 * current rendering, a new rendering of the node will be generated using 134 * the new source node and its rendering generated using that version of 135 * <code>RemoteRIF.create</code>() that updates the rendering of the node 136 * according to the specified <code>PropertyChangeEventJAI</code>. The 137 * identified tiles will be retained from the old rendering insofar as 138 * possible. This might involve for example adding tiles to a 139 * <code>TileCache</code> under the ownership of the new rendering. 140 * A <code>RenderingChangeEvent</code> will then be fired to all 141 * <code>PropertyChangeListener</code>s of the node, and to any node sinks 142 * that are <code>PropertyChangeListener</code>s. The 143 * <code>newRendering</code> parameter of the event constructor 144 * (which may be retrieved via the <code>getNewValue()</code> method of 145 * the event) will be set to either the new rendering of the node or to 146 * <code>null</code> if it was not possible to retain any tiles of the 147 * previous rendering. 148 * 149 * @see RenderedOp 150 * @see RemoteRenderedImage 151 * 152 * @since JAI 1.1 153 */ 154 public class RemoteRenderedOp extends RenderedOp 155 implements RemoteRenderedImage { 156 157 /** The name of the protocol this class provides an implementation for. */ 158 protected String protocolName; 159 160 /** The name of the server. */ 161 protected String serverName; 162 163 // The NegotiableCapabilitySet representing the negotiated values. 164 private NegotiableCapabilitySet negotiated; 165 166 /** 167 * The RenderingHints when the node was last rendered, i.e., when 168 * "theImage" was set to its current value. 169 */ 170 private transient RenderingHints oldHints; 171 172 /** Node event names. */ 173 private static Set nodeEventNames = null; 174 175 static { 176 nodeEventNames = new HashSet(); 177 nodeEventNames.add("protocolname"); 178 nodeEventNames.add("servername"); 179 nodeEventNames.add("protocolandservername"); 180 nodeEventNames.add("operationname"); 181 nodeEventNames.add("operationregistry"); 182 nodeEventNames.add("parameterblock"); 183 nodeEventNames.add("sources"); 184 nodeEventNames.add("parameters"); 185 nodeEventNames.add("renderinghints"); 186 } 187 188 /** 189 * Constructs a <code>RemoteRenderedOp</code> that will be used to 190 * instantiate a particular rendered operation to be performed remotely 191 * using the default operation registry, the name of the remote imaging 192 * protocol, the name of the server to perform the operation on, an 193 * operation name, a <code>ParameterBlock</code>, and a set of 194 * rendering hints. All input parameters are saved by reference. 195 * 196 * <p> An <code>IllegalArgumentException</code> may 197 * be thrown by the protocol specific classes at a later point, if 198 * null is provided as the serverName argument and null is not 199 * considered a valid server name by the specified protocol. 200 * 201 * <p> The <code>RenderingHints</code> may contain negotiation 202 * preferences specified under the <code>KEY_NEGOTIATION_PREFERENCES</code> 203 * key. 204 * 205 * @param protocolName The protocol name as a String. 206 * @param serverName The server name as a String. 207 * @param opName The operation name. 208 * @param pb The sources and parameters. If <code>null</code>, 209 * it is assumed that this node has no sources and 210 * parameters. 211 * @param hints The rendering hints. If <code>null</code>, it is 212 * assumed that no hints are associated with the 213 * rendering. 214 * 215 * @throws IllegalArgumentException if <code>protocolName</code> is 216 * <code>null</code>. 217 * @throws IllegalArgumentException if <code>opName</code> is 218 * <code>null</code>. 219 */ RemoteRenderedOp(String protocolName, String serverName, String opName, ParameterBlock pb, RenderingHints hints)220 public RemoteRenderedOp(String protocolName, 221 String serverName, 222 String opName, 223 ParameterBlock pb, 224 RenderingHints hints) { 225 this(null, protocolName, serverName, opName, pb, hints); 226 } 227 228 /** 229 * Constructs a <code>RemoteRenderedOp</code> that will be used to 230 * instantiate a particular rendered operation to be performed remotely 231 * using the specified operation registry, the name of the remote imaging 232 * protocol, the name of the server to perform the operation on, an 233 * operation name, a <code>ParameterBlock</code>, and a set of 234 * rendering hints. All input parameters are saved by reference. 235 * 236 * <p> An <code>IllegalArgumentException</code> may 237 * be thrown by the protocol specific classes at a later point, if 238 * null is provided as the serverName argument and null is not 239 * considered a valid server name by the specified protocol. 240 * 241 * <p> The <code>RenderingHints</code> may contain negotiation 242 * preferences specified under the <code>KEY_NEGOTIATION_PREFERENCES</code> 243 * key. 244 * 245 * @param registry The <code>OperationRegistry</code> to be used for 246 * instantiation. if <code>null</code>, the default 247 * registry is used. 248 * @param protocolName The protocol name as a String. 249 * @param serverName The server name as a String. 250 * @param opName The operation name. 251 * @param pb The sources and parameters. If <code>null</code>, 252 * it is assumed that this node has no sources and 253 * parameters. 254 * @param hints The rendering hints. If <code>null</code>, it is 255 * assumed that no hints are associated with the 256 * rendering. 257 * 258 * @throws IllegalArgumentException if <code>protocolName</code> is 259 * <code>null</code>. 260 * @throws IllegalArgumentException if <code>opName</code> is 261 * <code>null</code>. 262 */ RemoteRenderedOp(OperationRegistry registry, String protocolName, String serverName, String opName, ParameterBlock pb, RenderingHints hints)263 public RemoteRenderedOp(OperationRegistry registry, 264 String protocolName, 265 String serverName, 266 String opName, 267 ParameterBlock pb, 268 RenderingHints hints) { 269 270 // This will throw IAE for opName if null 271 super(registry, opName, pb, hints); 272 273 if (protocolName == null) 274 throw new IllegalArgumentException(JaiI18N.getString("Generic1")); 275 276 this.protocolName = protocolName; 277 this.serverName = serverName; 278 279 // Add the node as a PropertyChangeListener of itself for 280 // the critical attributes of the node. Superclass RenderedOp 281 // takes care of all critical attributes except the following. 282 // Case is ignored in the property names but infix caps are 283 // used here anyway. 284 addPropertyChangeListener("ServerName", this); 285 addPropertyChangeListener("ProtocolName", this); 286 addPropertyChangeListener("ProtocolAndServerName", this); 287 } 288 289 /** 290 * Returns the <code>String</code> that identifies the server. 291 */ getServerName()292 public String getServerName() { 293 return serverName; 294 } 295 296 /** 297 * Sets a <code>String</code> identifying the server. 298 * 299 * <p> If the supplied name does not equal the current server name, a 300 * <code>PropertyChangeEventJAI</code> named "ServerName" 301 * will be fired and a <code>RenderingChangeEvent</code> may be 302 * fired if the node has already been rendered. The oldValue 303 * field in the <code>PropertyChangeEventJAI</code> will contain 304 * the old server name <code>String</code> and the newValue 305 * field will contain the new server name <code>String</code>. 306 * 307 * @param serverName A <code>String</code> identifying the server. 308 * @throws IllegalArgumentException if serverName is null. 309 */ setServerName(String serverName)310 public void setServerName(String serverName) { 311 312 if (serverName == null) 313 throw new IllegalArgumentException(JaiI18N.getString("Generic2")); 314 315 if (serverName.equalsIgnoreCase(this.serverName)) return; 316 317 String oldServerName = this.serverName; 318 this.serverName = serverName; 319 fireEvent("ServerName", oldServerName, serverName); 320 nodeSupport.resetPropertyEnvironment(false); 321 } 322 323 /** 324 * Returns the <code>String</code> that identifies the remote imaging 325 * protocol. 326 */ getProtocolName()327 public String getProtocolName() { 328 return protocolName; 329 } 330 331 /** 332 * Sets a <code>String</code> identifying the remote imaging protocol. 333 * This method causes this <code>RemoteRenderedOp</code> to use 334 * the new protocol name with the server name set on this node 335 * previously. If the server is not compliant with the new 336 * protocol name, the <code>setProtocolAndServerNames()</code> 337 * method should be used to set a new protocol name and a compliant 338 * new server name at the same time. 339 * 340 * <p> If the supplied name does not equal the current protocol name, a 341 * <code>PropertyChangeEventJAI</code> named "ProtocolName" 342 * will be fired and a <code>RenderingChangeEvent</code> may be 343 * fired if the node has already been rendered. The oldValue 344 * field in the <code>PropertyChangeEventJAI</code> will contain 345 * the old protocol name <code>String</code> and the newValue 346 * field will contain the new protocol name <code>String</code>. 347 * 348 * @param protocolName A <code>String</code> identifying the server. 349 * @throws IllegalArgumentException if protocolName is null. 350 */ setProtocolName(String protocolName)351 public void setProtocolName(String protocolName) { 352 353 if (protocolName == null) 354 throw new IllegalArgumentException(JaiI18N.getString("Generic1")); 355 356 if (protocolName.equalsIgnoreCase(this.protocolName)) return; 357 358 String oldProtocolName = this.protocolName; 359 this.protocolName = protocolName; 360 fireEvent("ProtocolName", oldProtocolName, protocolName); 361 nodeSupport.resetPropertyEnvironment(false); 362 } 363 364 /** 365 * Sets the protocol name and the server name of this 366 * <code>RemoteRenderedOp</code> to the specified arguments.. 367 * 368 * <p> If both the supplied protocol name and the supplied server 369 * name values do not equal the current values, a 370 * <code>PropertyChangeEventJAI</code> named "ProtocolAndServerName" 371 * will be fired. The oldValue field in the 372 * <code>PropertyChangeEventJAI</code> will contain a two element 373 * array of <code>String</code>s, the old protocol name being the 374 * first element and the old server name being the second. Similarly 375 * the newValue field of the <code>PropertyChangeEventJAI</code> will 376 * contain a two element array of <code>String</code>s, the new protocol 377 * name being the first element and the new server name being the 378 * second. If only the supplied protocol name does not equal 379 * the current protocol name, a <code>PropertyChangeEventJAI</code> 380 * named "ProtocolName" will be fired. If only the supplied server 381 * name does not equal the current server name, a 382 * <code>PropertyChangeEventJAI</code> named "ServerName" 383 * will be fired. 384 * 385 * @param protocolName A <code>String</code> identifying the protocol. 386 * @param serverName A <code>String</code> identifying the server. 387 * @throws IllegalArgumentException if protocolName is null. 388 * @throws IllegalArgumentException if serverName is null. 389 */ setProtocolAndServerNames(String protocolName, String serverName)390 public void setProtocolAndServerNames(String protocolName, 391 String serverName) { 392 393 if (serverName == null) 394 throw new IllegalArgumentException(JaiI18N.getString("Generic2")); 395 396 if (protocolName == null) 397 throw new IllegalArgumentException(JaiI18N.getString("Generic1")); 398 399 boolean protocolNotChanged = 400 protocolName.equalsIgnoreCase(this.protocolName); 401 boolean serverNotChanged = 402 serverName.equalsIgnoreCase(this.serverName); 403 404 if (protocolNotChanged) { 405 if (serverNotChanged) 406 // Neither changed 407 return; 408 else { 409 // Only serverName changed 410 setServerName(serverName); 411 return; 412 } 413 } else { 414 if (serverNotChanged) { 415 // Only protocolName changed 416 setProtocolName(protocolName); 417 return; 418 } 419 } 420 421 String oldProtocolName = this.protocolName; 422 String oldServerName = this.serverName; 423 this.protocolName = protocolName; 424 this.serverName = serverName; 425 426 // Both changed 427 fireEvent("ProtocolAndServerName", 428 new String[] {oldProtocolName, oldServerName}, 429 new String[] {protocolName, serverName}); 430 nodeSupport.resetPropertyEnvironment(false); 431 } 432 433 /** 434 * Returns the name of the <code>RegistryMode</code> corresponding to 435 * this <code>RemoteRenderedOp</code>. This method overrides the 436 * implementation in <code>RenderedOp</code> to always returns the 437 * <code>String</code> "remoteRendered". 438 */ getRegistryModeName()439 public String getRegistryModeName() { 440 return RegistryMode.getMode("remoteRendered").getName(); 441 } 442 443 /** 444 * Overrides the <code>RenderedOp</code> method to allow the operation 445 * to be performed remotely. 446 */ createInstance(boolean isNodeRendered)447 protected synchronized PlanarImage createInstance(boolean isNodeRendered) { 448 449 ParameterBlock pb = new ParameterBlock(); 450 pb.setParameters(getParameters()); 451 452 int numSources = getNumSources(); 453 454 for (int i = 0; i < numSources; i++) { 455 456 Object source = getNodeSource(i); 457 Object ai = null; 458 if (source instanceof RenderedOp) { 459 460 RenderedOp src = (RenderedOp)source; 461 ai = isNodeRendered ? 462 src.getRendering() : 463 src.createInstance(); 464 465 } else if ((source instanceof RenderedImage) || 466 (source instanceof Collection)) { 467 468 ai = source; 469 } else if (source instanceof CollectionOp) { 470 ai = ((CollectionOp)source).getCollection(); 471 } else { 472 /* Source is some other type. Pass on (for now). */ 473 ai = source; 474 } 475 pb.addSource(ai); 476 } 477 478 RemoteRenderedImage instance = 479 RemoteRIFRegistry.create(nodeSupport.getRegistry(), 480 protocolName, 481 serverName, 482 nodeSupport.getOperationName(), 483 pb, 484 nodeSupport.getRenderingHints()); 485 486 // Throw an exception if the rendering is null. 487 if (instance == null) { 488 throw new ImagingException(JaiI18N.getString("RemoteRenderedOp2")); 489 } 490 491 // Save the state of the node. 492 RenderingHints rh = nodeSupport.getRenderingHints(); 493 oldHints = rh == null ? null : (RenderingHints)rh.clone(); 494 495 // Ensure that the rendering is a PlanarImage. 496 return PlanarImage.wrapRenderedImage(instance); 497 } 498 499 /* ----- PropertyChangeListener method. ----- */ 500 501 /** 502 * Implementation of <code>PropertyChangeListener</code>. 503 * 504 * <p> When invoked with an event which is an instance of 505 * <code>RenderingChangeEvent</code> the node will respond by 506 * re-rendering itself while retaining any tiles possible. 507 */ 508 // XXX Update javadoc both here and at class level. propertyChange(PropertyChangeEvent evt)509 public synchronized void propertyChange(PropertyChangeEvent evt) { 510 511 // 512 // React if and only if the node has been rendered and 513 // A: a non-PropertySourceChangeEvent PropertyChangeEventJAI 514 // was received from this node, or 515 // B: a RenderingChangeEvent was received from a source node. 516 // 517 518 // Cache event and node sources. 519 Object evtSrc = evt.getSource(); 520 Vector nodeSources = nodeSupport.getParameterBlock().getSources(); 521 522 // Get the name of the bean property and convert it to lower 523 // case now for efficiency later. 524 String propName = evt.getPropertyName().toLowerCase(Locale.ENGLISH); 525 526 if (theImage != null && 527 ((evt instanceof PropertyChangeEventJAI && 528 evtSrc == this && 529 !(evt instanceof PropertySourceChangeEvent) && 530 nodeEventNames.contains(propName)) || 531 ((evt instanceof RenderingChangeEvent || 532 evt instanceof CollectionChangeEvent || 533 (evt instanceof PropertyChangeEventJAI && 534 evtSrc instanceof RenderedImage && 535 propName.equals("invalidregion"))) && 536 nodeSources.contains(evtSrc)))) { 537 538 // Save the previous rendering. 539 PlanarImage theOldImage = theImage; 540 541 // Initialize the event flag. 542 boolean shouldFireEvent = false; 543 544 // Set default invalid region to null (the entire image). 545 Shape invalidRegion = null; 546 547 if (evtSrc == this && 548 (propName.equals("operationregistry") || 549 propName.equals("protocolname") || 550 propName.equals("protocolandservername"))) { 551 552 // invalidate the entire rendering. 553 shouldFireEvent = true; 554 theImage = null; 555 556 } else if (evt instanceof RenderingChangeEvent || 557 (evtSrc instanceof RenderedImage && 558 propName.equals("invalidregion"))) { 559 560 // Set the event flag. 561 shouldFireEvent = true; 562 Shape srcInvalidRegion = null; 563 564 if (evt instanceof RenderingChangeEvent) { 565 566 // RenderingChangeEvent presumably from a source 567 // RenderedOp. 568 RenderingChangeEvent rcEvent = (RenderingChangeEvent)evt; 569 570 // Get the invalidated region of the source. 571 srcInvalidRegion = rcEvent.getInvalidRegion(); 572 573 // If entire source is invalid replace with source bounds. 574 if (srcInvalidRegion == null) { 575 srcInvalidRegion = 576 ((PlanarImage)rcEvent.getOldValue()).getBounds(); 577 } 578 } else { 579 580 // Get the invalidated region of the source. 581 srcInvalidRegion = (Shape)evt.getNewValue(); 582 583 // If entire source is invalid replace with source bounds. 584 if (srcInvalidRegion == null) { 585 RenderedImage rSrc = (RenderedImage)evtSrc; 586 srcInvalidRegion = 587 new Rectangle(rSrc.getMinX(), rSrc.getMinY(), 588 rSrc.getWidth(), rSrc.getHeight()); 589 } 590 } 591 592 // Only process further if the rendering is a 593 // PlanarImageServerProxy. 594 if (!(theImage instanceof PlanarImageServerProxy)) { 595 596 // Clear the current rendering. 597 theImage = null; 598 599 } else { 600 601 // Save the previous rendering as a PlanarImageServerProxy. 602 PlanarImageServerProxy oldPISP = 603 (PlanarImageServerProxy)theImage; 604 605 // Cache source invalid bounds. 606 Rectangle srcInvalidBounds = srcInvalidRegion.getBounds(); 607 608 // If bounds are empty, replace srcInvalidRegion with 609 // the complement of the image bounds within the 610 // bounds of all tiles. 611 if (srcInvalidBounds.isEmpty()) { 612 int x = oldPISP.tileXToX(oldPISP.getMinTileX()); 613 int y = oldPISP.tileYToY(oldPISP.getMinTileY()); 614 int w = 615 oldPISP.getNumXTiles() * oldPISP.getTileWidth(); 616 int h = 617 oldPISP.getNumYTiles() * oldPISP.getTileHeight(); 618 Rectangle tileBounds = new Rectangle(x, y, w, h); 619 Rectangle imageBounds = oldPISP.getBounds(); 620 if (!tileBounds.equals(imageBounds)) { 621 Area tmpArea = new Area(tileBounds); 622 tmpArea.subtract(new Area(imageBounds)); 623 srcInvalidRegion = tmpArea; 624 srcInvalidBounds = srcInvalidRegion.getBounds(); 625 } 626 } 627 628 // ----- Determine invalid destination region. ----- 629 630 boolean saveAllTiles = false; 631 ArrayList validTiles = null; 632 if (srcInvalidBounds.isEmpty()) { 633 invalidRegion = srcInvalidRegion; 634 saveAllTiles = true; 635 636 } else { 637 638 // Get index of source which changed. 639 int idx = nodeSources.indexOf(evtSrc); 640 641 // Determine bounds of invalid destination region. 642 Rectangle dstRegionBounds = 643 oldPISP.mapSourceRect(srcInvalidBounds, idx); 644 645 if (dstRegionBounds == null) { 646 dstRegionBounds = oldPISP.getBounds(); 647 } 648 649 // Determine invalid destination region. 650 Point[] indices = getTileIndices(dstRegionBounds); 651 int numIndices = indices != null ? indices.length : 0; 652 GeneralPath gp = null; 653 654 for(int i = 0; i < numIndices; i++) { 655 if (i % 1000 == 0 && gp != null) 656 gp = new GeneralPath(new Area(gp)); 657 658 Rectangle dstRect = 659 getTileRect(indices[i].x, indices[i].y); 660 Rectangle srcRect = 661 oldPISP.mapDestRect(dstRect, idx); 662 if(srcRect == null) { 663 gp = null; 664 break; 665 } 666 if(srcInvalidRegion.intersects(srcRect)) { 667 if(gp == null) { 668 gp = new GeneralPath(dstRect); 669 } else { 670 gp.append(dstRect, false); 671 } 672 } else { 673 if(validTiles == null) { 674 validTiles = new ArrayList(); 675 } 676 validTiles.add(indices[i]); 677 } 678 } 679 680 invalidRegion = (gp == null) ? null : new Area(gp); 681 } 682 683 // Retrieve the old TileCache. 684 TileCache oldCache = oldPISP.getTileCache(); 685 theImage = null; 686 687 // Only perform further processing if there is a cache 688 // and there are tiles to save. 689 if (oldCache != null && 690 (saveAllTiles || validTiles != null)) { 691 692 // Create new rendering 693 newEventRendering(protocolName, 694 oldPISP, 695 (PropertyChangeEventJAI)evt); 696 697 // Only perform further processing if the new 698 // rendering is an OpImage with a non-null TileCache. 699 if (theImage instanceof PlanarImageServerProxy && 700 ((PlanarImageServerProxy)theImage).getTileCache() != 701 null) { 702 PlanarImageServerProxy newPISP = 703 (PlanarImageServerProxy)theImage; 704 TileCache newCache = newPISP.getTileCache(); 705 706 Object tileCacheMetric = 707 newPISP.getTileCacheMetric(); 708 709 if (saveAllTiles) { 710 Raster[] tiles = oldCache.getTiles(oldPISP); 711 int numTiles = tiles == null ? 712 0 : tiles.length; 713 for(int i = 0; i < numTiles; i++) { 714 Raster tile = tiles[i]; 715 int tx = newPISP.XToTileX(tile.getMinX()); 716 int ty = newPISP.YToTileY(tile.getMinY()); 717 newCache.add(newPISP, 718 tx, ty, tile, 719 tileCacheMetric); 720 } 721 } else { // save some, but not all, tiles 722 int numValidTiles = validTiles.size(); 723 for(int i = 0; i < numValidTiles; i++) { 724 Point tileIndex = (Point)validTiles.get(i); 725 Raster tile = 726 oldCache.getTile(oldPISP, 727 tileIndex.x, 728 tileIndex.y); 729 if (tile != null) { 730 newCache.add(newPISP, 731 tileIndex.x, 732 tileIndex.y, 733 tile, 734 tileCacheMetric); 735 } 736 } 737 } 738 } 739 } 740 } 741 } else { // not op name or registry change nor RenderingChangeEvent 742 ParameterBlock oldPB = null; 743 ParameterBlock newPB = null; 744 String oldServerName = serverName; 745 String newServerName = serverName; 746 747 boolean checkInvalidRegion = false; 748 749 if (propName.equals("operationname")) { 750 751 if (theImage instanceof PlanarImageServerProxy) { 752 newEventRendering(protocolName, 753 (PlanarImageServerProxy)theImage, 754 (PropertyChangeEventJAI)evt); 755 } else { 756 theImage = null; 757 createRendering(); 758 } 759 760 // Do not set checkInvalidRegion to true, since there 761 // are no tiles to save for this case. 762 763 shouldFireEvent = true; 764 765 // XXX Do we need to do any evaluation of any 766 // DeferredData parameters. 767 768 } else if (propName.equals("parameterblock")) { 769 oldPB = (ParameterBlock)evt.getOldValue(); 770 newPB = (ParameterBlock)evt.getNewValue(); 771 checkInvalidRegion = true; 772 } else if (propName.equals("sources")) { 773 // Replace source(s) 774 Vector params = 775 nodeSupport.getParameterBlock().getParameters(); 776 oldPB = new ParameterBlock((Vector)evt.getOldValue(), 777 params); 778 newPB = new ParameterBlock((Vector)evt.getNewValue(), 779 params); 780 checkInvalidRegion = true; 781 } else if (propName.equals("parameters")) { 782 // Replace parameter(s) 783 oldPB = new ParameterBlock(nodeSources, 784 (Vector)evt.getOldValue()); 785 newPB = new ParameterBlock(nodeSources, 786 (Vector)evt.getNewValue()); 787 checkInvalidRegion = true; 788 } else if (propName.equals("renderinghints")) { 789 oldPB = newPB = nodeSupport.getParameterBlock(); 790 checkInvalidRegion = true; 791 } else if (propName.equals("servername")) { 792 oldPB = newPB = nodeSupport.getParameterBlock(); 793 oldServerName = (String)evt.getOldValue(); 794 newServerName = (String)evt.getNewValue(); 795 checkInvalidRegion = true; 796 } else if (evt instanceof CollectionChangeEvent) { 797 // Event from a CollectionOp source. 798 // Replace appropriate source. 799 int collectionIndex = nodeSources.indexOf(evtSrc); 800 Vector oldSources = (Vector)nodeSources.clone(); 801 Vector newSources = (Vector)nodeSources.clone(); 802 oldSources.set(collectionIndex, evt.getOldValue()); 803 newSources.set(collectionIndex, evt.getNewValue()); 804 805 Vector params = 806 nodeSupport.getParameterBlock().getParameters(); 807 808 oldPB = new ParameterBlock(oldSources, params); 809 newPB = new ParameterBlock(newSources, params); 810 811 checkInvalidRegion = true; 812 } 813 814 if (checkInvalidRegion) { 815 // Set event flag. 816 shouldFireEvent = true; 817 818 // Get the associated RemoteDescriptor. 819 OperationRegistry registry = nodeSupport.getRegistry(); 820 RemoteDescriptor odesc = (RemoteDescriptor) 821 registry.getDescriptor(RemoteDescriptor.class, 822 protocolName); 823 824 // XXX 825 // Evaluate any DeferredData parameters. 826 oldPB = ImageUtil.evaluateParameters(oldPB); 827 newPB = ImageUtil.evaluateParameters(newPB); 828 829 // Determine the invalid region. 830 invalidRegion = (Shape) 831 odesc.getInvalidRegion("rendered", 832 oldServerName, 833 oldPB, 834 oldHints, 835 newServerName, 836 newPB, 837 nodeSupport.getRenderingHints(), 838 this); 839 840 if (invalidRegion == null || 841 !(theImage instanceof PlanarImageServerProxy)) { 842 // Can't save any tiles; clear the rendering. 843 theImage = null; 844 845 } else { 846 847 // Create a new rendering. 848 PlanarImageServerProxy oldRendering = 849 (PlanarImageServerProxy)theImage; 850 851 newEventRendering(protocolName, oldRendering, 852 (PropertyChangeEventJAI)evt); 853 854 // If the new rendering is also a 855 // PlanarImageServerProxy, save some tiles. 856 if (theImage instanceof PlanarImageServerProxy && 857 oldRendering.getTileCache() != null && 858 ((PlanarImageServerProxy)theImage).getTileCache() 859 != null) { 860 PlanarImageServerProxy newRendering = 861 (PlanarImageServerProxy)theImage; 862 863 TileCache oldCache = oldRendering.getTileCache(); 864 TileCache newCache = newRendering.getTileCache(); 865 866 Object tileCacheMetric = 867 newRendering.getTileCacheMetric(); 868 869 // If bounds are empty, replace invalidRegion with 870 // the complement of the image bounds within the 871 // bounds of all tiles. 872 if (invalidRegion.getBounds().isEmpty()) { 873 int x = oldRendering.tileXToX( 874 oldRendering.getMinTileX()); 875 int y = oldRendering.tileYToY( 876 oldRendering.getMinTileY()); 877 int w = oldRendering.getNumXTiles() * 878 oldRendering.getTileWidth(); 879 int h = oldRendering.getNumYTiles() * 880 oldRendering.getTileHeight(); 881 Rectangle tileBounds = 882 new Rectangle(x, y, w, h); 883 Rectangle imageBounds = 884 oldRendering.getBounds(); 885 if (!tileBounds.equals(imageBounds)) { 886 Area tmpArea = new Area(tileBounds); 887 tmpArea.subtract(new Area(imageBounds)); 888 invalidRegion = tmpArea; 889 } 890 } 891 892 if (invalidRegion.getBounds().isEmpty()) { 893 894 // Save all tiles. 895 Raster[] tiles = 896 oldCache.getTiles(oldRendering); 897 int numTiles = tiles == null ? 898 0 : tiles.length; 899 for(int i = 0; i < numTiles; i++) { 900 Raster tile = tiles[i]; 901 int tx = 902 newRendering.XToTileX(tile.getMinX()); 903 int ty = 904 newRendering.YToTileY(tile.getMinY()); 905 newCache.add(newRendering, 906 tx, ty, tile, 907 tileCacheMetric); 908 } 909 } else { 910 // Copy tiles not in invalid region from old 911 // TileCache to new TileCache. 912 Raster[] tiles = 913 oldCache.getTiles(oldRendering); 914 int numTiles = tiles == null ? 915 0 : tiles.length; 916 for(int i = 0; i < numTiles; i++) { 917 Raster tile = tiles[i]; 918 Rectangle bounds = tile.getBounds(); 919 if (!invalidRegion.intersects(bounds)) { 920 newCache.add( 921 newRendering, 922 newRendering.XToTileX(bounds.x), 923 newRendering.YToTileY(bounds.y), 924 tile, 925 tileCacheMetric); 926 } 927 } 928 } 929 } 930 } 931 } 932 } 933 934 // Re-render the node. This will only occur if theImage 935 // has been set to null above. 936 if (theOldImage instanceof PlanarImageServerProxy && 937 theImage == null) { 938 newEventRendering(protocolName, 939 (PlanarImageServerProxy)theOldImage, 940 (PropertyChangeEventJAI)evt); 941 } else { 942 createRendering(); 943 } 944 945 // Fire an event if the flag was set. 946 if (shouldFireEvent) { 947 948 // Clear the synthetic and cached properties and reset the 949 // property source. 950 resetProperties(true); 951 952 // Create the event object. 953 RenderingChangeEvent rcEvent = 954 new RenderingChangeEvent(this, theOldImage, theImage, 955 invalidRegion); 956 957 // Fire to all registered listeners. 958 eventManager.firePropertyChange(rcEvent); 959 960 // Fire an event to all PropertyChangeListener sinks. 961 Vector sinks = getSinks(); 962 if (sinks != null) { 963 int numSinks = sinks.size(); 964 for (int i = 0; i < numSinks; i++) { 965 Object sink = sinks.get(i); 966 if (sink instanceof PropertyChangeListener) { 967 ((PropertyChangeListener)sink).propertyChange(rcEvent); 968 } 969 } 970 } 971 } 972 } 973 } 974 975 /** 976 * Creates a new rendering in response to the provided event, and 977 * assigns the new rendering to "theImage" variable. 978 */ newEventRendering(String protocolName, PlanarImageServerProxy oldPISP, PropertyChangeEventJAI event)979 private void newEventRendering(String protocolName, 980 PlanarImageServerProxy oldPISP, 981 PropertyChangeEventJAI event) { 982 RemoteRIF rrif = (RemoteRIF)nodeSupport.getRegistry(). 983 getFactory("remoterendered", protocolName); 984 theImage = (PlanarImage)rrif.create(oldPISP, this, event); 985 } 986 987 /** 988 * Fire an events to all registered listeners. 989 */ fireEvent(String propName, Object oldVal, Object newVal)990 private void fireEvent(String propName, Object oldVal, Object newVal) { 991 if (eventManager != null) { 992 Object eventSource = eventManager.getPropertyChangeEventSource(); 993 PropertyChangeEventJAI evt = 994 new PropertyChangeEventJAI(eventSource, 995 propName, oldVal, newVal); 996 eventManager.firePropertyChange(evt); 997 } 998 } 999 1000 /** 1001 * Returns the amount of time between retries in milliseconds. 1002 * 1003 * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its 1004 * <code>getRetryInterval()</code> method will be called to return 1005 * the current retry interval. If no rendering has been created, and 1006 * a value was set using the <code>setRetryInterval()</code> method), that 1007 * value will be returned, else the default retry interval as defined by 1008 * <code>RemoteJAI.DEFAULT_RETRY_INTERVAL</code> is returned. 1009 */ getRetryInterval()1010 public int getRetryInterval() { 1011 if (theImage != null) { 1012 return ((RemoteRenderedImage)theImage).getRetryInterval(); 1013 } else { 1014 RenderingHints rh = nodeSupport.getRenderingHints(); 1015 if (rh == null) { 1016 return RemoteJAI.DEFAULT_RETRY_INTERVAL; 1017 } else { 1018 Integer i = (Integer)rh.get(JAI.KEY_RETRY_INTERVAL); 1019 if (i == null) 1020 return RemoteJAI.DEFAULT_RETRY_INTERVAL; 1021 else 1022 return i.intValue(); 1023 } 1024 } 1025 } 1026 1027 /** 1028 * Sets the amount of time between retries in milliseconds. If this 1029 * <code>RemoteRenderedOp</code> has already been rendered, the 1030 * <code>setRetryInterval()</code> method is called on the rendering 1031 * to inform it of the new retry interval. The rendering can choose to 1032 * ignore this new setting, in which case <code>getRetryInterval()</code> 1033 * will still return the old value, or the rendering can honor these 1034 * settings, in which case <code>getRetryInterval()</code> will return 1035 * the new settings. If this <code>RemoteRenderedOp</code> has not been 1036 * rendered, the new retry interval specified will be stored. 1037 * These new stored retry interval will be passed as 1038 * part of the <code>RenderingHints</code> using the 1039 * <code>KEY_RETRY_INTERVAL</code> key, to the new rendering 1040 * when it is created. 1041 * 1042 * @param retryInterval The amount of time (in milliseconds) to wait 1043 * between retries. 1044 * @throws IllegalArgumentException if retryInterval is negative. 1045 */ setRetryInterval(int retryInterval)1046 public void setRetryInterval(int retryInterval) { 1047 1048 if (retryInterval < 0) 1049 throw new IllegalArgumentException(JaiI18N.getString("Generic3")); 1050 1051 if (theImage != null) { 1052 ((RemoteRenderedImage)theImage).setRetryInterval(retryInterval); 1053 } 1054 1055 RenderingHints rh = nodeSupport.getRenderingHints(); 1056 if (rh == null) { 1057 nodeSupport.setRenderingHints(new RenderingHints(null)); 1058 rh = nodeSupport.getRenderingHints(); 1059 } 1060 1061 rh.put(JAI.KEY_RETRY_INTERVAL, new Integer(retryInterval)); 1062 } 1063 1064 /** 1065 * Returns the number of retries. 1066 * 1067 * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its 1068 * <code>getNumRetries()</code> method will be called to return 1069 * the current number of retries. If no rendering has been created, and 1070 * a value was set using the <code>setNumRetries()</code> method), that 1071 * value will be returned, else the default retry interval as defined by 1072 * <code>RemoteJAI.DEFAULT_NUM_RETRIES</code> is returned. 1073 */ getNumRetries()1074 public int getNumRetries() { 1075 if (theImage != null) { 1076 return ((RemoteRenderedImage)theImage).getNumRetries(); 1077 } else { 1078 RenderingHints rh = nodeSupport.getRenderingHints(); 1079 if (rh == null) { 1080 return RemoteJAI.DEFAULT_NUM_RETRIES; 1081 } else { 1082 Integer i = (Integer)rh.get(JAI.KEY_NUM_RETRIES); 1083 if (i == null) 1084 return RemoteJAI.DEFAULT_NUM_RETRIES; 1085 else 1086 return i.intValue(); 1087 } 1088 } 1089 } 1090 1091 /** 1092 * Sets the number of retries. If this <code>RemoteRenderedOp</code> 1093 * has already been rendered, the <code>setNumRetries()</code> method 1094 * is called on the rendering to inform it of the new number of retries. 1095 * The rendering can choose to ignore these new settings, in which case 1096 * <code>getNunRetries()</code> will still return the old values, or 1097 * the rendering can honor these new settings in which 1098 * case <code>getNumRetries()</code> will return the new value. 1099 * If this <code>RemoteRenderedOp</code> has not been rendered, 1100 * the new setting specified will be stored. 1101 * These new settings which have been stored will be passed as 1102 * part of the <code>RenderingHints</code> using the 1103 * <code>KEY_NUM_RETRIES</code> key, to the new rendering 1104 * when it is created. 1105 * 1106 * @param numRetries The number of times an operation should be retried 1107 * in case of a network error. 1108 * @throws IllegalArgumentException if numRetries is negative. 1109 */ setNumRetries(int numRetries)1110 public void setNumRetries(int numRetries) { 1111 1112 if (numRetries < 0) 1113 throw new IllegalArgumentException( 1114 JaiI18N.getString("Generic4")); 1115 1116 if (theImage != null) { 1117 ((RemoteRenderedImage)theImage).setNumRetries(numRetries); 1118 } 1119 1120 RenderingHints rh = nodeSupport.getRenderingHints(); 1121 if (rh == null) { 1122 nodeSupport.setRenderingHints(new RenderingHints(null)); 1123 rh = nodeSupport.getRenderingHints(); 1124 } 1125 1126 rh.put(JAI.KEY_NUM_RETRIES, new Integer(numRetries)); 1127 } 1128 1129 /** 1130 * Sets the preferences to be used in the client-server 1131 * communication. These preferences are utilized in the negotiation 1132 * process. Note that preferences for more than one category can be 1133 * specified using this method. Also each preference can be a list 1134 * of values in decreasing order of preference, each value specified 1135 * as a <code>NegotiableCapability</code>. The 1136 * <code>NegotiableCapability</code> first (for a particular category) 1137 * in this list is given highest priority in the negotiation process 1138 * (for that category). 1139 * 1140 * <p> It may be noted that this method allows for multiple negotiation 1141 * cycles by allowing negotiation preferences to be set 1142 * multiple times. If this <code>RemoteRenderedOp</code> has already 1143 * been rendered, the <code>setNegotiationPreferences()</code> method 1144 * is called on the rendering to inform it of the new preferences. The 1145 * rendering can choose to ignore these new preferences, in which case 1146 * <code>getNegotiatedValues()</code> will still return the results of 1147 * the old negotiation, or the rendering can re-perform the negotiation, 1148 * (using the <code>RemoteJAI.negotiate</code>, for example) in which 1149 * case <code>getNegotiatedValues()</code> will return the new 1150 * negotiated values. If this <code>RemoteRenderedOp</code> has not been 1151 * rendered, the new preferences specified will be stored, a negotiation 1152 * with these new preferences will be initiated and the results stored. 1153 * These new preferences which have been stored will be passed as 1154 * part of the <code>RenderingHints</code> using the 1155 * <code>KEY_NEGOTIATION_PREFERENCES</code> key, to the new rendering 1156 * when it is created. 1157 * 1158 * @param preferences The preferences to be used in the negotiation 1159 * process. 1160 */ setNegotiationPreferences(NegotiableCapabilitySet preferences)1161 public void setNegotiationPreferences(NegotiableCapabilitySet preferences) 1162 { 1163 if (theImage != null) { 1164 ((RemoteRenderedImage)theImage).setNegotiationPreferences( 1165 preferences); 1166 } 1167 1168 RenderingHints rh = nodeSupport.getRenderingHints(); 1169 1170 if (preferences != null) { 1171 if (rh == null) { 1172 nodeSupport.setRenderingHints(new RenderingHints(null)); 1173 rh = nodeSupport.getRenderingHints(); 1174 } 1175 1176 rh.put(JAI.KEY_NEGOTIATION_PREFERENCES, preferences); 1177 } else { 1178 // Remove any previous values set for negotiation preferences 1179 if (rh != null) { 1180 rh.remove(JAI.KEY_NEGOTIATION_PREFERENCES); 1181 } 1182 } 1183 1184 negotiated = negotiate(preferences); 1185 } 1186 1187 /** 1188 * Returns the current negotiation preferences or null, if none were 1189 * set previously. 1190 */ getNegotiationPreferences()1191 public NegotiableCapabilitySet getNegotiationPreferences() { 1192 1193 RenderingHints rh = nodeSupport.getRenderingHints(); 1194 return rh == null ? null : (NegotiableCapabilitySet)rh.get( 1195 JAI.KEY_NEGOTIATION_PREFERENCES); 1196 } 1197 1198 // do the negotiation negotiate(NegotiableCapabilitySet prefs)1199 private NegotiableCapabilitySet negotiate(NegotiableCapabilitySet prefs) { 1200 1201 OperationRegistry registry = nodeSupport.getRegistry(); 1202 1203 NegotiableCapabilitySet serverCap = null; 1204 1205 // Get the RemoteDescriptor for protocolName 1206 RemoteDescriptor descriptor = (RemoteDescriptor) 1207 registry.getDescriptor(RemoteDescriptor.class, protocolName); 1208 1209 if (descriptor == null) { 1210 Object[] msgArg0 = {new String(protocolName)}; 1211 MessageFormat formatter = new MessageFormat(""); 1212 formatter.setLocale(Locale.getDefault()); 1213 formatter.applyPattern(JaiI18N.getString("RemoteJAI16")); 1214 throw new ImagingException(formatter.format(msgArg0)); 1215 } 1216 1217 int count=0; 1218 int numRetries = getNumRetries(); 1219 int retryInterval = getRetryInterval(); 1220 1221 Exception rieSave = null; 1222 while (count++ < numRetries) { 1223 try { 1224 serverCap = descriptor.getServerCapabilities(serverName); 1225 break; 1226 } catch (RemoteImagingException rie) { 1227 // Print that an Exception occured 1228 System.err.println(JaiI18N.getString("RemoteJAI24")); 1229 rieSave = rie; 1230 // Sleep for retryInterval milliseconds 1231 try { 1232 Thread.sleep(retryInterval); 1233 } catch (InterruptedException ie) { 1234 // throw new RuntimeException(ie.toString()); 1235 sendExceptionToListener(JaiI18N.getString("Generic5"), 1236 new ImagingException(JaiI18N.getString("Generic5"), ie)); 1237 } 1238 } 1239 } 1240 1241 if (serverCap == null && count > numRetries) { 1242 sendExceptionToListener(JaiI18N.getString("RemoteJAI18"), rieSave); 1243 // throw new RemoteImagingException(JaiI18N.getString("RemoteJAI18")+"\n"+rieSave.getMessage()); 1244 } 1245 1246 RemoteRIF rrif = (RemoteRIF)registry.getFactory("remoteRendered", 1247 protocolName); 1248 1249 return RemoteJAI.negotiate(prefs, 1250 serverCap, 1251 rrif.getClientCapabilities()); 1252 } 1253 1254 /** 1255 * Returns the results of the negotiation between the client and server 1256 * capabilities according to the preferences set via the 1257 * <code>setNegotiationPreferences()</code> method. This will return 1258 * null if no negotiation preferences were set, and no negotiation 1259 * was performed, or if the negotiation failed. 1260 * 1261 * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its 1262 * <code>getNegotiatedValues()</code> method will be called to return 1263 * the current negotiated values. If no rendering has been created, then 1264 * the internally stored negotiated value (calculated when the new 1265 * preferences were set using the <code>setNegotiationPreferences()</code> 1266 * method) will be returned. 1267 */ getNegotiatedValues()1268 public NegotiableCapabilitySet getNegotiatedValues() 1269 throws RemoteImagingException { 1270 if (theImage != null) { 1271 return ((RemoteRenderedImage)theImage).getNegotiatedValues(); 1272 } else { 1273 return negotiated; 1274 } 1275 } 1276 1277 /** 1278 * Returns the results of the negotiation between the client and server 1279 * capabilities for the given category according to the preferences 1280 * set via the <code>setNegotiationPreferences()</code> method. This 1281 * will return null if no negotiation preferences were set, and no 1282 * negotiation was performed, or if the negotiation failed. 1283 * 1284 * <p> If this <code>RemoteRenderedOp</code> has been rendered, then its 1285 * <code>getNegotiatedValues()</code> method will be called to return 1286 * the current negotiated values. If no rendering has been created, then 1287 * the internally stored negotiated value (calculated when the new 1288 * preferences were set using the <code>setNegotiationPreferences()</code> 1289 * method) will be returned. 1290 * 1291 * @param category The category to return the negotiated results for. 1292 */ getNegotiatedValue(String category)1293 public NegotiableCapability getNegotiatedValue(String category) 1294 throws RemoteImagingException { 1295 if (theImage != null) { 1296 return ((RemoteRenderedImage)theImage).getNegotiatedValue( 1297 category); 1298 } else { 1299 return negotiated == null ? null : 1300 negotiated.getNegotiatedValue(category); 1301 } 1302 } 1303 1304 /** 1305 * Informs the server of the negotiated values that are the result of 1306 * a successful negotiation. If this <code>RemoteRenderedOp</code> has 1307 * been rendered, then the rendering's 1308 * <code>setServerNegotiatedValues</code> method will be called to 1309 * inform the server of the negotiated results. If no rendering has 1310 * been created, this method will do nothing. 1311 * 1312 * @param negotiatedValues The result of the negotiation. 1313 */ setServerNegotiatedValues(NegotiableCapabilitySet negotiatedValues)1314 public void setServerNegotiatedValues(NegotiableCapabilitySet 1315 negotiatedValues) 1316 throws RemoteImagingException { 1317 1318 if (theImage != null) { 1319 ((RemoteRenderedImage)theImage).setServerNegotiatedValues( 1320 negotiatedValues); 1321 } 1322 } 1323 sendExceptionToListener(String message, Exception e)1324 void sendExceptionToListener(String message, Exception e) { 1325 ImagingListener listener = 1326 (ImagingListener)getRenderingHints().get(JAI.KEY_IMAGING_LISTENER); 1327 1328 listener.errorOccurred(message, e, this, false); 1329 } 1330 } 1331