2
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
4
* Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
6
* The contents of this file are subject to the terms of either the GNU
7
* General Public License Version 2 only ("GPL") or the Common
8
* Development and Distribution License("CDDL") (collectively, the
9
* "License"). You may not use this file except in compliance with the
10
* License. You can obtain a copy of the License at
11
* http://www.netbeans.org/cddl-gplv2.html
12
* or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
13
* specific language governing permissions and limitations under the
14
* License. When distributing the software, include this License Header
15
* Notice in each file and include the License file at
16
* nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
17
* particular file as subject to the "Classpath" exception as provided
18
* by Sun in the GPL Version 2 section of the License file that
19
* accompanied this code. If applicable, add the following below the
20
* License Header, with the fields enclosed by brackets [] replaced by
21
* your own identifying information:
22
* "Portions Copyrighted [year] [name of copyright owner]"
26
* The Original Software is NetBeans. The Initial Developer of the Original
27
* Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
28
* Microsystems, Inc. All Rights Reserved.
30
* If you wish your version of this file to be governed by only the CDDL
31
* or only the GPL Version 2, indicate your decision by adding
32
* "[Contributor] elects to include this software in this distribution
33
* under the [CDDL or GPL Version 2] license." If you do not indicate a
34
* single choice of license, a recipient has the option to distribute
35
* your version of this file under either the CDDL, the GPL Version 2 or
36
* to extend the choice of license to its licensees as provided above.
37
* However, if you add GPL Version 2 code and therefore, elected the GPL
38
* Version 2 license, then the option applies only if the new code is
39
* made subject to such option by the copyright holder.
42
package org.netbeans.modules.pdf;
44
import java.awt.event.ActionEvent;
45
import java.awt.event.ActionListener;
47
import java.io.IOException;
48
import java.net.MalformedURLException;
50
import java.net.URISyntaxException;
52
import javax.swing.JMenuItem;
53
import org.openide.DialogDisplayer;
54
import org.openide.ErrorManager;
55
import org.openide.NotifyDescriptor;
56
import org.openide.awt.Mnemonics;
57
import org.openide.cookies.InstanceCookie;
58
import org.openide.filesystems.FileObject;
59
import org.openide.filesystems.FileUtil;
60
import org.openide.loaders.XMLDataObject;
61
import org.openide.modules.InstalledFileLocator;
62
import org.openide.util.NbBundle;
63
import org.w3c.dom.Document;
64
import org.w3c.dom.Element;
65
import org.w3c.dom.Node;
66
import org.w3c.dom.NodeList;
70
* Permits a special kind of .xml file to be used for PDF links.
71
* After this processor is registered, any .xml file which matches
72
* the specified DTD (it must declare a <code><!DOCTYPE></code>)
73
* will provide an instance of a {@link JMenuItem}.
74
* This menu item will be named according to the XML file's display
75
* name (which may be controlled via localized filenames from a
76
* bundle as elsewhere).
77
* Selecting it will try to show the mentioned PDF file.
78
* The PDF file may be referred to as an absolute file name,
79
* or as a localized path within the IDE installation,
80
* or (in the future) as an arbitrary URL.
81
* The XML file is suitable for direct inclusion in a menu
82
* bar folder, for example <samp>..../system/Menu/Help/</samp>.
85
* @author Marian Petras
86
* @see org.openide.loaders.XMLDataObject.Processor
88
public class LinkProcessor implements InstanceCookie,
89
XMLDataObject.Processor,
92
/** Public ID of catalog. */
93
public static final String PUBLIC_ID
94
= "-//NetBeans//DTD PDF Document Menu Link 1.0//EN"; //NOI18N
96
public static final String PUBLIC_WWW
97
= "http://www.netbeans.org/dtds/pdf_link-1_0.dtd"; //NOI18N
99
/** <code>XMLDataObject</code> this processor is linked to. */
100
private XMLDataObject xmlDataObject;
103
/* JST: Replaced with registration in xml layer.
104
* Initilializes <code>LinkProcessor</code>. *
105
public static void init () {
106
// Registering of catalog is in xml layer, see org/netbeans/modules/utilities/Layer.xml.
108
XMLDataObject.Info xmlInfo = new XMLDataObject.Info ();
110
xmlInfo.setIconBase("/org/netbeans/modules/pdf/PDFDataIcon"); // NOI18N
111
xmlInfo.addProcessorClass(LinkProcessor.class);
112
XMLDataObject.registerInfo(PUBLIC_ID, xmlInfo);
116
/* Implements interface <code>XMLDataObject.Processor</code>. */
118
* Attaches this processor to specified XML data object.
120
* @param xmlDataObject XML data object to which attach this processor
122
public void attachTo(XMLDataObject xmlDataObject) {
123
this.xmlDataObject = xmlDataObject;
126
/* Implements interface <code>InstanceCookie</code>. */
128
* @return <code>JMenuItem</code> class
130
public Class instanceClass() throws IOException, ClassNotFoundException {
131
return JMenuItem.class;
134
/* Implements interface <code>InstanceCookie</code>. */
135
public Object instanceCreate() throws IOException, ClassNotFoundException {
137
Image icon = Utilities.loadImage(
138
"org/netbeans/modules/pdf/PDFDataIcon.gif"); //NOI18N
140
FileObject file = xmlDataObject.getPrimaryFile();
141
FileSystem.Status fsStatus = file.getFileSystem().getStatus();
142
icon = fsStatus.annotateIcon(icon,
143
BeanInfo.ICON_COLOR_16x16,
144
xmlDataObject.files());
145
} catch (FileStateInvalidException fsie) {
146
// OK, so we use the default icon
149
String name = xmlDataObject.getNodeDelegate().getDisplayName();
151
JMenuItem menuItem = new JMenuItem(/*new ImageIcon(icon)*/);
152
Mnemonics.setLocalizedText(menuItem, name);
153
menuItem.addActionListener(this);
158
/* Implements interface <code>InstanceCookie</code>. */
160
* @return name of the <code>xmlDataObject</code>
162
public String instanceName() {
163
return xmlDataObject.getName();
167
* Retrieves the name of a file describing the XML data object
169
* @return as much precious path to the file as possible
171
private String getXMLFileName() {
172
FileObject fileObject = xmlDataObject.getPrimaryFile();
173
return FileUtil.getFileDisplayName(fileObject);
177
* Notifies the user that the XML file is broken.
179
private void notifyXMLFileBroken() {
180
String msg = NbBundle.getMessage(LinkProcessor.class,
181
"EXC_file_not_matching_DTD", //NOI18N
183
ErrorManager.getDefault().log(ErrorManager.USER, msg);
187
* Notifies the user about some problem with the XML file.
189
* @param msgKey resource bundle key for the message
190
* @param urlSpec url that caused the problem
191
* @param isError type of the message - use <code>true</code> for
192
* an error message, <code>false</code> for
193
* an information message
195
private void notifyBadFileSpec(String msgKey,
198
String msg = NbBundle.getMessage(LinkProcessor.class,
202
ErrorManager.getDefault().log(isError ? ErrorManager.WARNING
209
private void notifyFileDoesNotExist(String path) {
210
String msg = NbBundle.getMessage(LinkProcessor.class,
211
"MSG_File_does_not_exist", //NOI18N
213
DialogDisplayer.getDefault().notify(new NotifyDescriptor.Message(
214
msg, NotifyDescriptor.WARNING_MESSAGE));
218
* Grabs a file from a specification in an element of an XML file.
220
* @param innerElement element containing specification of a PDF file
221
* @return file corresponding to the specification,
222
* or <code>null</code> if the specification was illegal
225
private File grabFile(Element innerElement) {
226
String linkType = innerElement.getTagName();
228
/* handle element "file": */
229
if (linkType.equals("file")) { //NOI18N
230
if (!innerElement.hasAttribute("path")) { //NOI18N
231
notifyXMLFileBroken();
234
return new File(innerElement.getAttribute("path")); //NOI18N
236
/* handle element "idefile": */
237
} else if (linkType.equals("idefile")) { //NOI18N
238
if (!innerElement.hasAttribute("base")) { //NOI18N
239
notifyXMLFileBroken();
242
String base = innerElement.getAttribute("base"); //NOI18N
243
String path = base.replace('.', '/') + ".pdf"; //NOI18N
244
File file = InstalledFileLocator.getDefault()
245
.locate(path, null, true);
247
notifyFileDoesNotExist(path);
252
/* handle element "url": */
253
} else if (linkType.equals("url")) { //NOI18N
254
if (!innerElement.hasAttribute("name")) { //NOI18N
255
notifyXMLFileBroken();
258
String urlSpec = innerElement.getAttribute("name"); //NOI18N
261
url = new URL(urlSpec);
262
} catch (MalformedURLException ex) {
264
"MSG_Cannot_open_malformed_URL", //NOI18N
269
if (!url.getProtocol().equals("file")) { //NOI18N
271
"MSG_Cannot_open_unsupported_URL", //NOI18N
276
return new File(new URI("file://" + url.getPath())); //NOI18N
277
} catch (URISyntaxException ex1) {
278
ErrorManager.getDefault().notify(ex1);
280
} catch (IllegalArgumentException ex2) {
281
ErrorManager.getDefault().notify(ex2);
286
notifyXMLFileBroken();
291
/* Implements interface <code>ActionListener</code>. */
293
* Performs an action. Retrieves a PDF data object from the specified
294
* XML data object and opens it.
296
public void actionPerformed(ActionEvent evt) {
299
/* Grab the element containing the link: */
300
Element innerElement;
301
Document document = xmlDataObject.getDocument();
302
Element pdfLinkElement = document.getDocumentElement();
303
NodeList nodeList = pdfLinkElement.getChildNodes();
304
int count = nodeList.getLength();
306
for (int i = 0; i < count; i++) {
307
Node nextNode = nodeList.item(i);
308
if (nextNode.getNodeType() == Node.ELEMENT_NODE) {
312
/* there should be just one element */
313
notifyXMLFileBroken();
319
/* there should be exactly one element within 'pdfLink' */
320
notifyXMLFileBroken();
323
innerElement = (Element) node;
325
/* Retrieve the PDF file: */
326
File file = grabFile(innerElement);
328
/* Try to open the file in an external viewer: */
331
// [PENDING] in-process PDF viewer support
332
new PDFOpenSupport(file).open();
334
} catch (IllegalArgumentException ex) {
335
notifyFileDoesNotExist(file.getPath());
338
} catch (Exception e) {
339
ErrorManager.getDefault().notify(e);