source: josm/trunk/src/org/openstreetmap/josm/gui/widgets/JosmImageView.java@ 12218

Last change on this file since 12218 was 12218, checked in by Don-vip, 7 years ago

code cleanup - catch ReflectiveOperationException when possible

File size: 6.1 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.widgets;
3
4import java.awt.Graphics;
5import java.awt.Image;
6import java.awt.Shape;
7import java.lang.reflect.Field;
8import java.lang.reflect.InvocationTargetException;
9import java.lang.reflect.Method;
10import java.net.URL;
11
12import javax.swing.ImageIcon;
13import javax.swing.text.AttributeSet;
14import javax.swing.text.Element;
15import javax.swing.text.html.ImageView;
16
17import org.openstreetmap.josm.Main;
18import org.openstreetmap.josm.tools.ImageProvider;
19import org.openstreetmap.josm.tools.Utils;
20
21/**
22 * Specialized Image View allowing to display SVG images.
23 * @since 8933
24 */
25public class JosmImageView extends ImageView {
26
27 private static final int LOADING_FLAG = 1;
28 private static final int WIDTH_FLAG = 4;
29 private static final int HEIGHT_FLAG = 8;
30 private static final int RELOAD_FLAG = 16;
31 private static final int RELOAD_IMAGE_FLAG = 32;
32
33 private final Field imageField;
34 private final Field stateField;
35 private final Field widthField;
36 private final Field heightField;
37
38 /**
39 * Constructs a new {@code JosmImageView}.
40 * @param elem the element to create a view for
41 * @throws SecurityException see {@link Class#getDeclaredField} for details
42 * @throws NoSuchFieldException see {@link Class#getDeclaredField} for details
43 */
44 public JosmImageView(Element elem) throws NoSuchFieldException {
45 super(elem);
46 imageField = ImageView.class.getDeclaredField("image");
47 stateField = ImageView.class.getDeclaredField("state");
48 widthField = ImageView.class.getDeclaredField("width");
49 heightField = ImageView.class.getDeclaredField("height");
50 Utils.setObjectsAccessible(imageField, stateField, widthField, heightField);
51 }
52
53 /**
54 * Makes sure the necessary properties and image is loaded.
55 */
56 private void doSync() {
57 try {
58 int s = (int) stateField.get(this);
59 if ((s & RELOAD_IMAGE_FLAG) != 0) {
60 doRefreshImage();
61 }
62 s = (int) stateField.get(this);
63 if ((s & RELOAD_FLAG) != 0) {
64 synchronized (this) {
65 stateField.set(this, ((int) stateField.get(this) | RELOAD_FLAG) ^ RELOAD_FLAG);
66 }
67 setPropertiesFromAttributes();
68 }
69 } catch (IllegalArgumentException | ReflectiveOperationException | SecurityException e) {
70 Main.error(e);
71 }
72 }
73
74 /**
75 * Loads the image and updates the size accordingly. This should be
76 * invoked instead of invoking <code>loadImage</code> or
77 * <code>updateImageSize</code> directly.
78 * @throws IllegalAccessException see {@link Field#set} and {@link Method#invoke} for details
79 * @throws IllegalArgumentException see {@link Field#set} and {@link Method#invoke} for details
80 * @throws InvocationTargetException see {@link Method#invoke} for details
81 * @throws NoSuchMethodException see {@link Class#getDeclaredMethod} for details
82 * @throws SecurityException see {@link Class#getDeclaredMethod} for details
83 */
84 private void doRefreshImage() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
85 synchronized (this) {
86 // clear out width/height/reloadimage flag and set loading flag
87 stateField.set(this, ((int) stateField.get(this) | LOADING_FLAG | RELOAD_IMAGE_FLAG | WIDTH_FLAG |
88 HEIGHT_FLAG) ^ (WIDTH_FLAG | HEIGHT_FLAG |
89 RELOAD_IMAGE_FLAG));
90 imageField.set(this, null);
91 widthField.set(this, 0);
92 heightField.set(this, 0);
93 }
94
95 try {
96 // Load the image
97 doLoadImage();
98
99 // And update the size params
100 Method updateImageSize = ImageView.class.getDeclaredMethod("updateImageSize");
101 Utils.setObjectsAccessible(updateImageSize);
102 updateImageSize.invoke(this);
103 } finally {
104 synchronized (this) {
105 // Clear out state in case someone threw an exception.
106 stateField.set(this, ((int) stateField.get(this) | LOADING_FLAG) ^ LOADING_FLAG);
107 }
108 }
109 }
110
111 /**
112 * Loads the image from the URL <code>getImageURL</code>. This should
113 * only be invoked from <code>refreshImage</code>.
114 * @throws IllegalAccessException see {@link Field#set} and {@link Method#invoke} for details
115 * @throws IllegalArgumentException see {@link Field#set} and {@link Method#invoke} for details
116 * @throws InvocationTargetException see {@link Method#invoke} for details
117 * @throws NoSuchMethodException see {@link Class#getDeclaredMethod} for details
118 * @throws SecurityException see {@link Class#getDeclaredMethod} for details
119 */
120 private void doLoadImage() throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
121 URL src = getImageURL();
122 if (src != null) {
123 String urlStr = src.toExternalForm();
124 if (urlStr.endsWith(".svg") || urlStr.endsWith(".svg?format=raw")) {
125 ImageIcon imgIcon = new ImageProvider(urlStr).setOptional(true).get();
126 imageField.set(this, imgIcon != null ? imgIcon.getImage() : null);
127 } else {
128 Method loadImage = ImageView.class.getDeclaredMethod("loadImage");
129 Utils.setObjectsAccessible(loadImage);
130 loadImage.invoke(this);
131 }
132 } else {
133 imageField.set(this, null);
134 }
135 }
136
137 @Override
138 public Image getImage() {
139 doSync();
140 return super.getImage();
141 }
142
143 @Override
144 public AttributeSet getAttributes() {
145 doSync();
146 return super.getAttributes();
147 }
148
149 @Override
150 public void paint(Graphics g, Shape a) {
151 doSync();
152 super.paint(g, a);
153 }
154
155 @Override
156 public float getPreferredSpan(int axis) {
157 doSync();
158 return super.getPreferredSpan(axis);
159 }
160
161 @Override
162 public void setSize(float width, float height) {
163 doSync();
164 super.setSize(width, height);
165 }
166}
Note: See TracBrowser for help on using the repository browser.