001    /*
002     * $Id: LinkModel.java,v 1.9 2006/05/14 15:55:55 dmouse Exp $
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;
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        public boolean equals(Object obj) {
269            if (this == obj) {
270                return true;
271            }
272            if (obj != null && obj instanceof LinkModel) {
273                LinkModel other = (LinkModel) obj;
274                if (!getText().equals(other.getText())) {
275                    return false;
276                }
277    
278                if (!getTarget().equals(other.getTarget())) {
279                    return false;
280                }
281    
282                if (!getURL().equals(other.getURL())) {
283                    return false;
284                }
285                return true;
286            }
287            return false;
288        }
289    
290        @Override
291        public int hashCode() {
292            int result = 7;
293    
294            result = 37 * result + ((getText() == null) ? 0 : getText().hashCode());
295            result = 37 * result
296                    + ((getTarget() == null) ? 1 : getTarget().hashCode());
297            result = 37 * result + ((getURL() == null) ? 2 : getURL().hashCode());
298    
299            return result;
300        }
301    
302        @Override
303        public String toString() {
304    
305            StringBuffer buffer = new StringBuffer("[");
306            // RG: Fix for J2SE 5.0; Can't cascade append() calls because
307            // return type in StringBuffer and AbstractStringBuilder are different
308            buffer.append("url=");
309            buffer.append(url);
310            buffer.append(", target=");
311            buffer.append(target);
312            buffer.append(", text=");
313            buffer.append(text);
314            buffer.append("]");
315    
316            return buffer.toString();
317        }
318    
319    }