001 /*
002 * $Id: LinkModel.java 2951 2008-06-17 10:07:49Z kleopatra $
003 *
004 * Copyright 2004 Sun Microsystems, Inc., 4150 Network Circle,
005 * Santa Clara, California 95054, U.S.A. All rights reserved.
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * You should have received a copy of the GNU Lesser General Public
018 * License along with this library; if not, write to the Free Software
019 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
020 */
021
022 package org.jdesktop.swingx.hyperlink;
023
024 import java.beans.PropertyChangeListener;
025 import java.beans.PropertyChangeSupport;
026 import java.net.MalformedURLException;
027 import java.net.URL;
028 import java.util.logging.Logger;
029
030 /**
031 * An bean which represents an URL link.
032 *
033 * Text, URL and visited are bound properties. Compares by Text.
034 *
035 * @author Mark Davidson
036 * @author Jeanette Winzenburg
037 */
038 public class LinkModel implements Comparable {
039
040 private static final Logger LOG = Logger.getLogger(LinkModel.class
041 .getName());
042
043 private String text; // display text
044
045 private URL url; // url of the link
046
047 private String target; // url target frame
048
049 private boolean visited = false;
050
051 private PropertyChangeSupport propertyChangeSupport;
052
053 public static final String VISITED_PROPERTY = "visited";
054
055 // hack - this class assumes that the url always != null
056 // need to cleanup
057 private static String defaultURLString = "https://jdnc.dev.java.net";
058
059 private static URL defaultURL;
060
061 /**
062 *
063 * @param text
064 * @param target
065 * @param url
066 */
067 public LinkModel(String text, String target, URL url) {
068 setText(text);
069 setTarget(target);
070 setURL(url != null ? url : getDefaultURL());
071 }
072
073 public LinkModel() {
074 this(" ", null, null);
075 }
076
077 public LinkModel(String text) {
078 this(text, null, null);
079 }
080
081 /**
082 * @param text text to that a renderer would display
083 * @param target the target that a URL should load into.
084 * @param template a string that represents a URL with
085 * &{N} place holders for string substitution
086 * @param args an array of strings which will be used for substitition
087 */
088 public LinkModel(String text, String target, String template, String[] args) {
089 setText(text);
090 setTarget(target);
091 setURL(createURL(template, args));
092 }
093
094 /**
095 * Set the display text.
096 */
097 public void setText(String text) {
098 String old = getText();
099 this.text = text;
100 firePropertyChange("text", old, getText());
101 }
102
103 public String getText() {
104 if (text != null) {
105 return text;
106 } else if (url != null) {
107 return getURL().toString();
108 }
109 return null;
110 }
111
112 public void setURLString(String howToURLString) {
113 URL url = null;
114 try {
115 url = new URL(howToURLString);
116 } catch (MalformedURLException e) {
117 url = getDefaultURL();
118 LOG.warning("the given urlString is malformed: " + howToURLString +
119 "\n falling back to default url: " + url);
120 }
121 setURL(url);
122 }
123
124 private URL getDefaultURL() {
125 if (defaultURL == null) {
126 try {
127 defaultURL = new URL(defaultURLString);
128 } catch (MalformedURLException e) {
129 LOG.fine("should not happen - defaultURL is wellFormed: "
130 + defaultURLString);
131 }
132 }
133 return defaultURL;
134 }
135
136 /**
137 * Set the url and resets the visited flag.
138 *
139 * Think: keep list of visited urls here?
140 */
141 public void setURL(URL url) {
142 if (url == null) {
143 throw new IllegalArgumentException("URL for link cannot be null");
144 }
145 if (url.equals(getURL()))
146 return;
147 URL old = getURL();
148 this.url = url;
149 firePropertyChange("URL", old, url);
150 setVisited(false);
151 }
152
153 public URL getURL() {
154 return url;
155 }
156
157 /**
158 * Create a URL from a template string that has place holders and an array
159 * of strings which will be substituted into the place holders. The place
160 * holders are represented as
161 *
162 * @{N} where N = { 1..n }
163 * <p>
164 * For example, if the template contains a string like:
165 * http://bugz.sfbay/cgi-bin/showbug?cat=@{1}&sub_cat=@{2} and a two
166 * arg array contains: java, classes_swing The resulting URL will be:
167 * http://bugz.sfbay/cgi-bin/showbug?cat=java&sub_cat=classes_swing
168 * <p>
169 * @param template a url string that contains the placeholders
170 * @param args an array of strings that will be substituted
171 */
172 private URL createURL(String template, String[] args) {
173 URL url = null;
174 try {
175 String urlStr = template;
176 for (int i = 0; i < args.length; i++) {
177 urlStr = urlStr.replaceAll("@\\{" + (i + 1) + "\\}", args[i]);
178 }
179 url = new URL(urlStr);
180 } catch (MalformedURLException ex) {
181 //
182 }
183 return url;
184 }
185
186 /**
187 * Set the target that the URL should load into. This can be a uri
188 * representing another control or the name of a window or special targets.
189 * See: http://www.w3c.org/TR/html401/present/frames.html#adef-target
190 */
191 public void setTarget(String target) {
192 this.target = target;
193 }
194
195 /**
196 * Return the target for the URL.
197 *
198 * @return value of the target. If null then "_blank" will be returned.
199 */
200 public String getTarget() {
201 if (target != null) {
202 return target;
203 } else {
204 return "_blank";
205 }
206 }
207
208 /**
209 * Sets a flag to indicate if the link has been visited. The state of this
210 * flag can be used to render the color of the link.
211 */
212 public void setVisited(boolean visited) {
213 boolean old = getVisited();
214 this.visited = visited;
215 firePropertyChange(VISITED_PROPERTY, old, getVisited());
216 }
217
218 public boolean getVisited() {
219 return visited;
220 }
221
222 // ---------------------- property change notification
223
224 public void addPropertyChangeListener(PropertyChangeListener l) {
225 getPropertyChangeSupport().addPropertyChangeListener(l);
226
227 }
228
229 public void removePropertyChangeListener(PropertyChangeListener l) {
230 if (propertyChangeSupport == null)
231 return;
232 propertyChangeSupport.removePropertyChangeListener(l);
233 }
234
235 protected void firePropertyChange(String property, Object oldValue,
236 Object newValue) {
237 if (propertyChangeSupport == null)
238 return;
239 propertyChangeSupport.firePropertyChange(property, oldValue, newValue);
240 }
241
242 protected void firePropertyChange(String property, boolean oldValue,
243 boolean newValue) {
244 if (propertyChangeSupport == null)
245 return;
246 propertyChangeSupport.firePropertyChange(property, oldValue, newValue);
247
248 }
249
250 private PropertyChangeSupport getPropertyChangeSupport() {
251 if (propertyChangeSupport == null) {
252 propertyChangeSupport = new PropertyChangeSupport(this);
253 }
254 return propertyChangeSupport;
255 }
256
257 // Comparable interface for sorting.
258 public int compareTo(Object obj) {
259 if (obj == null) {
260 return 1;
261 }
262 if (obj == this) {
263 return 0;
264 }
265 return text.compareTo(((LinkModel) obj).text);
266 }
267
268 @Override
269 public boolean equals(Object obj) {
270 if (this == obj) {
271 return true;
272 }
273 if (obj != null && obj instanceof LinkModel) {
274 LinkModel other = (LinkModel) obj;
275 if (!getText().equals(other.getText())) {
276 return false;
277 }
278
279 if (!getTarget().equals(other.getTarget())) {
280 return false;
281 }
282
283 return getURL().equals(other.getURL());
284 }
285 return false;
286 }
287
288 @Override
289 public int hashCode() {
290 int result = 7;
291
292 result = 37 * result + ((getText() == null) ? 0 : getText().hashCode());
293 result = 37 * result
294 + ((getTarget() == null) ? 1 : getTarget().hashCode());
295 result = 37 * result + ((getURL() == null) ? 2 : getURL().hashCode());
296
297 return result;
298 }
299
300 @Override
301 public String toString() {
302
303 StringBuffer buffer = new StringBuffer("[");
304 // RG: Fix for J2SE 5.0; Can't cascade append() calls because
305 // return type in StringBuffer and AbstractStringBuilder are different
306 buffer.append("url=");
307 buffer.append(url);
308 buffer.append(", target=");
309 buffer.append(target);
310 buffer.append(", text=");
311 buffer.append(text);
312 buffer.append("]");
313
314 return buffer.toString();
315 }
316
317 }