1/*
2 * Copyright (c) 1994, 2002, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26package sun.net.www;
27import java.net.URL;
28import java.io.*;
29import java.util.StringTokenizer;
30
31public class MimeEntry implements Cloneable {
32    private String typeName;    // of the form: "type/subtype"
33    private String tempFileNameTemplate;
34
35    private int action;
36    private String command;
37    private String description;
38    private String imageFileName;
39    private String fileExtensions[];
40
41    boolean starred;
42
43    // Actions
44    public static final int             UNKNOWN                 = 0;
45    public static final int             LOAD_INTO_BROWSER       = 1;
46    public static final int             SAVE_TO_FILE            = 2;
47    public static final int             LAUNCH_APPLICATION      = 3;
48
49    static final String[] actionKeywords = {
50        "unknown",
51        "browser",
52        "save",
53        "application",
54    };
55
56    /**
57     * Construct an empty entry of the given type and subtype.
58     */
59    public MimeEntry(String type) {
60        // Default action is UNKNOWN so clients can decide what the default
61        // should be, typically save to file or ask user.
62        this(type, UNKNOWN, null, null, null);
63    }
64
65    //
66    // The next two constructors are used only by the deprecated
67    // PlatformMimeTable classes or, in last case, is called by the public
68    // constructor.  They are kept here anticipating putting support for
69    // mailcap formatted config files back in (so BOTH the properties format
70    // and the mailcap formats are supported).
71    //
72    MimeEntry(String type, String imageFileName, String extensionString) {
73        typeName = type.toLowerCase();
74        action = UNKNOWN;
75        command = null;
76        this.imageFileName = imageFileName;
77        setExtensions(extensionString);
78        starred = isStarred(typeName);
79    }
80
81    // For use with MimeTable::parseMailCap
82    MimeEntry(String typeName, int action, String command,
83              String tempFileNameTemplate) {
84        this.typeName = typeName.toLowerCase();
85        this.action = action;
86        this.command = command;
87        this.imageFileName = null;
88        this.fileExtensions = null;
89
90        this.tempFileNameTemplate = tempFileNameTemplate;
91    }
92
93    // This is the one called by the public constructor.
94    MimeEntry(String typeName, int action, String command,
95              String imageFileName, String fileExtensions[]) {
96
97        this.typeName = typeName.toLowerCase();
98        this.action = action;
99        this.command = command;
100        this.imageFileName = imageFileName;
101        this.fileExtensions = fileExtensions;
102
103        starred = isStarred(typeName);
104
105    }
106
107    public synchronized String getType() {
108        return typeName;
109    }
110
111    public synchronized void setType(String type) {
112        typeName = type.toLowerCase();
113    }
114
115    public synchronized int getAction() {
116        return action;
117    }
118
119    public synchronized void setAction(int action, String command) {
120        this.action = action;
121        this.command = command;
122    }
123
124    public synchronized void setAction(int action) {
125        this.action = action;
126    }
127
128    public synchronized String getLaunchString() {
129        return command;
130    }
131
132    public synchronized void setCommand(String command) {
133        this.command = command;
134    }
135
136    public synchronized String getDescription() {
137        return (description != null ? description : typeName);
138    }
139
140    public synchronized void setDescription(String description) {
141        this.description = description;
142    }
143
144    // ??? what to return for the image -- the file name or should this return
145    // something more advanced like an image source or something?
146    // returning the name has the least policy associated with it.
147    // pro tempore, we'll use the name
148    public String getImageFileName() {
149        return imageFileName;
150    }
151
152    public synchronized void setImageFileName(String filename) {
153        File file = new File(filename);
154        if (file.getParent() == null) {
155            imageFileName = System.getProperty(
156                                     "java.net.ftp.imagepath."+filename);
157        }
158        else {
159            imageFileName = filename;
160        }
161
162        if (filename.lastIndexOf('.') < 0) {
163            imageFileName = imageFileName + ".gif";
164        }
165    }
166
167    public String getTempFileTemplate() {
168        return tempFileNameTemplate;
169    }
170
171    public synchronized String[] getExtensions() {
172        return fileExtensions;
173    }
174
175    public synchronized String getExtensionsAsList() {
176        String extensionsAsString = "";
177        if (fileExtensions != null) {
178            for (int i = 0; i < fileExtensions.length; i++) {
179                extensionsAsString += fileExtensions[i];
180                if (i < (fileExtensions.length - 1)) {
181                    extensionsAsString += ",";
182                }
183            }
184        }
185
186        return extensionsAsString;
187    }
188
189    public synchronized void setExtensions(String extensionString) {
190        StringTokenizer extTokens = new StringTokenizer(extensionString, ",");
191        int numExts = extTokens.countTokens();
192        String extensionStrings[] = new String[numExts];
193
194        for (int i = 0; i < numExts; i++) {
195            String ext = (String)extTokens.nextElement();
196            extensionStrings[i] = ext.trim();
197        }
198
199        fileExtensions = extensionStrings;
200    }
201
202    private boolean isStarred(String typeName) {
203        return (typeName != null)
204            && (typeName.length() > 0)
205            && (typeName.endsWith("/*"));
206    }
207
208    /**
209     * Invoke the MIME type specific behavior for this MIME type.
210     * Returned value can be one of several types:
211     * <ol>
212     * <li>A thread -- the caller can choose when to launch this thread.
213     * <li>A string -- the string is loaded into the browser directly.
214     * <li>An input stream -- the caller can read from this byte stream and
215     *     will typically store the results in a file.
216     * <li>A document (?) --
217     * </ol>
218     */
219    public Object launch(java.net.URLConnection urlc, InputStream is, MimeTable mt) throws ApplicationLaunchException {
220        switch (action) {
221        case SAVE_TO_FILE:
222            // REMIND: is this really the right thing to do?
223            try {
224                return is;
225            } catch(Exception e) {
226                // I18N
227                return "Load to file failed:\n" + e;
228            }
229
230        case LOAD_INTO_BROWSER:
231            // REMIND: invoke the content handler?
232            // may be the right thing to do, may not be -- short term
233            // where docs are not loaded asynch, loading and returning
234            // the content is the right thing to do.
235            try {
236                return urlc.getContent();
237            } catch (Exception e) {
238                return null;
239            }
240
241        case LAUNCH_APPLICATION:
242            {
243                String threadName = command;
244                int fst = threadName.indexOf(' ');
245                if (fst > 0) {
246                    threadName = threadName.substring(0, fst);
247                }
248
249                return new MimeLauncher(this, urlc, is,
250                                        mt.getTempFileTemplate(), threadName);
251            }
252
253        case UNKNOWN:
254            // REMIND: What do do here?
255            return null;
256        }
257
258        return null;
259    }
260
261    public boolean matches(String type) {
262        if (starred) {
263          // REMIND: is this the right thing or not?
264          return type.startsWith(typeName);
265        } else {
266            return type.equals(typeName);
267        }
268    }
269
270    public Object clone() {
271        // return a shallow copy of this.
272        MimeEntry theClone = new MimeEntry(typeName);
273        theClone.action = action;
274        theClone.command = command;
275        theClone.description = description;
276        theClone.imageFileName = imageFileName;
277        theClone.tempFileNameTemplate = tempFileNameTemplate;
278        theClone.fileExtensions = fileExtensions;
279
280        return theClone;
281    }
282
283    public synchronized String toProperty() {
284        StringBuffer buf = new StringBuffer();
285
286        String separator = "; ";
287        boolean needSeparator = false;
288
289        int action = getAction();
290        if (action != MimeEntry.UNKNOWN) {
291            buf.append("action=" + actionKeywords[action]);
292            needSeparator = true;
293        }
294
295        String command = getLaunchString();
296        if (command != null && command.length() > 0) {
297            if (needSeparator) {
298                buf.append(separator);
299            }
300            buf.append("application=" + command);
301            needSeparator = true;
302        }
303
304        if (getImageFileName() != null) {
305            if (needSeparator) {
306                buf.append(separator);
307            }
308            buf.append("icon=" + getImageFileName());
309            needSeparator = true;
310        }
311
312        String extensions = getExtensionsAsList();
313        if (extensions.length() > 0) {
314            if (needSeparator) {
315                buf.append(separator);
316            }
317            buf.append("file_extensions=" + extensions);
318            needSeparator = true;
319        }
320
321        String description = getDescription();
322        if (description != null && !description.equals(getType())) {
323            if (needSeparator) {
324                buf.append(separator);
325            }
326            buf.append("description=" + description);
327        }
328
329        return buf.toString();
330    }
331
332    public String toString() {
333        return "MimeEntry[contentType=" + typeName
334            + ", image=" + imageFileName
335            + ", action=" + action
336            + ", command=" + command
337            + ", extensions=" + getExtensionsAsList()
338            + "]";
339    }
340}
341