001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */
017:
018: /*
019: * MimeType.java
020: *
021: * represents an object that specifies the MIME type for DocFlavor object
022: */
023:
024: package org.apache.harmony.x.print;
025:
026: import java.io.Serializable;
027: import java.util.Vector;
028:
029: public class MimeType implements Serializable, Cloneable {
030:
031: static final long serialVersionUID = -1062742668693502508L;
032:
033: private String aType = null; // Media type
034: private String aSubtype = null; // Media subtype
035: private String params[][]; // parameters names and values array
036:
037: public MimeType(String mimeType) {
038: parseString(mimeType);
039: }
040:
041: public String getType() {
042: return aType;
043: }
044:
045: public String getSubtype() {
046: return aSubtype;
047: }
048:
049: public String[][] getParams() {
050: return params;
051: }
052:
053: /*
054: * gets parameter value for the given parameter bane,
055: * returns null if such parameter is absent
056: */
057: public String getParameter(String paramName) {
058: for (int i = 0; i < params.length; i++) {
059: if ((params[i][0]).equals(paramName)) {
060: return params[i][1];
061: }
062: }
063: return null;
064: }
065:
066: public boolean equals(Object obj) {
067: return (obj != null)
068: && (obj instanceof MimeType)
069: && (getCanonicalForm().equals(((MimeType) obj)
070: .getCanonicalForm()));
071: }
072:
073: public int hashCode() {
074: return getCanonicalForm().hashCode();
075: }
076:
077: public String toString() {
078: return getCanonicalForm();
079: }
080:
081: /*
082: * returns canonical for MimeType object.
083: */
084: public String getCanonicalForm() {
085: StringBuffer s = new StringBuffer();
086: s.append(aType);
087: s.append("/");
088: s.append(aSubtype);
089: for (int i = 0; i < params.length; i++) {
090: s.append("; ");
091: s.append(params[i][0]);
092: s.append("=\"");
093: s.append(params[i][1]);
094: s.append("\"");
095: }
096: return s.toString();
097: }
098:
099: //-------------------------------------------------------------------------------------
100:
101: /*
102: * Parses MIME-type content header string as it is described in RFC 2045,
103: * 822. Header may contain content type, subtype, parameters separated by
104: * ";" and comments in brackets. Type and subtype are mandatory fields and
105: * can not be omitted. All character should be US-ASCII. Type, subtype and
106: * parameter names are case insencitive, so I always convert them to lower
107: * case. Parameter names should not be duplicated. Parameter values can be
108: * case sencitive, it depends on parameter. "Charset" parameter is always
109: * case insencitive, so I always convert it to the lower case. I do not
110: * convert any other parameter values. Mime string tokens can not include
111: * some special ASCII characters - see isSpecialChar(char) function for
112: * more details. Parameter value can include special chars, however in this
113: * case it should be quotated. In canonical form all parameter values should
114: * be quotated and ordered by parameter names.
115: *
116: * RFC sources: http://www.rfc.net/rfc2045.html
117: * http://rfc.net/rfc822.html
118: */
119: private void parseString(String aString) {
120: String errMsg = "Illegal mime-type string format!";
121:
122: int state = 1; // state for parser
123: int nextState = 0; // next state after the comments end
124:
125: // convert String to char array
126: char s[] = new char[aString.length()];
127: aString.getChars(0, aString.length(), s, 0);
128:
129: int len = s.length - 1;
130:
131: int cnt1 = 1; // nesting comments level
132:
133: // Position of type and subtype in the string:
134: int startTypeIndex = 0;
135: int startSubtypeIndex = 0;
136:
137: // Analized parameter name and value variables:
138: int startParamNameIndex = 0;
139: int startParamValueIndex = 0;
140: String paramName = null;
141: String paramValue = null;
142:
143: Vector nameVector = new Vector(); // parameter names vector
144: Vector valueVector = new Vector(); // parameter values vector
145:
146: // string should not be empty, should have at least three characters
147: if (len <= 1) {
148: throw new IllegalArgumentException(errMsg);
149: }
150:
151: for (int i = 0; i <= len; i++) {
152: switch (state) {
153: case 1:
154: if (i == len) {
155: throw new IllegalArgumentException(errMsg);
156: }
157: if (isOKTokenChar(s[i])) {
158: startTypeIndex = i;
159: state = 2;
160: break;
161: }
162: if (isSpaceChar(s[i])) {
163: break;
164: }
165: if (s[i] == '(') {
166: nextState = 1;
167: state = 50;
168: break;
169: }
170: throw new IllegalArgumentException(errMsg);
171:
172: case 2:
173: if (i == len) {
174: throw new IllegalArgumentException(errMsg);
175: }
176: if (isOKTokenChar(s[i])) {
177: break;
178: }
179: if (s[i] == '/') {
180: state = 3;
181: aType = newLowercaseString(s, startTypeIndex, i);
182: break;
183: }
184: if (s[i] == '(') {
185: nextState = 4;
186: state = 50;
187: aType = newLowercaseString(s, startTypeIndex, i);
188: break;
189: }
190: if (isSpaceChar(s[i])) {
191: state = 4;
192: aType = newLowercaseString(s, startTypeIndex, i);
193: break;
194: }
195: throw new IllegalArgumentException(errMsg);
196:
197: case 3:
198: if (i == len) {
199: throw new IllegalArgumentException(errMsg);
200: }
201: if (isOKTokenChar(s[i])) {
202: startSubtypeIndex = i;
203: state = 5;
204: break;
205: }
206: if (s[i] == '(') {
207: nextState = 3;
208: state = 50;
209: break;
210: }
211: if (isSpaceChar(s[i])) {
212: break;
213: }
214: throw new IllegalArgumentException(errMsg);
215:
216: case 4:
217: if (i == len) {
218: throw new IllegalArgumentException(errMsg);
219: }
220: if (s[i] == '(') {
221: nextState = 4;
222: state = 50;
223: break;
224: }
225: if (isSpaceChar(s[i])) {
226: break;
227: }
228: if (s[i] == '/') {
229: startSubtypeIndex = i + 1;
230: state = 3;
231: break;
232: }
233: throw new IllegalArgumentException(errMsg);
234:
235: case 5:
236: if (isOKTokenChar(s[i])) {
237: if (i == len) {
238: aSubtype = newLowercaseString(s,
239: startSubtypeIndex, i + 1);
240: }
241: break;
242: }
243: if ((s[i] == ';') && (i != len)) {
244: aSubtype = newLowercaseString(s, startSubtypeIndex,
245: i);
246: state = 7;
247: break;
248: }
249: if ((s[i] == '(') && (i != len)) {
250: aSubtype = newLowercaseString(s, startSubtypeIndex,
251: i);
252: nextState = 6;
253: state = 50;
254: break;
255: }
256: if (isSpaceChar(s[i])) {
257: aSubtype = newLowercaseString(s, startSubtypeIndex,
258: i);
259: state = 6;
260: break;
261: }
262: throw new IllegalArgumentException(errMsg);
263:
264: case 6:
265: if (isSpaceChar(s[i])) {
266: break;
267: }
268: if ((s[i] == '(') && (i != len)) {
269: nextState = 6;
270: state = 50;
271: break;
272: }
273: if ((s[i] == ';') && (i != len)) {
274: state = 7;
275: break;
276: }
277: throw new IllegalArgumentException(errMsg);
278:
279: case 7:
280: if (i == len) {
281: throw new IllegalArgumentException(errMsg);
282: }
283: if (isSpaceChar(s[i])) {
284: break;
285: }
286: if (s[i] == '(') {
287: state = 50;
288: nextState = 7;
289: break;
290: }
291: startParamNameIndex = i;
292: if (isOKTokenChar(s[i])) {
293: state = 8;
294: break;
295: }
296: throw new IllegalArgumentException(errMsg);
297:
298: case 8:
299: if (i == len) {
300: throw new IllegalArgumentException(errMsg);
301: }
302: if (isOKTokenChar(s[i])) {
303: break;
304: }
305: if (isSpaceChar(s[i])) {
306: paramName = newLowercaseString(s,
307: startParamNameIndex, i);
308: state = 10;
309: break;
310: }
311: if (s[i] == '(') {
312: paramName = newLowercaseString(s,
313: startParamNameIndex, i);
314: state = 50;
315: nextState = 10;
316: break;
317: }
318: if (s[i] == '=') {
319: paramName = newLowercaseString(s,
320: startParamNameIndex, i);
321: state = 9;
322: break;
323: }
324: throw new IllegalArgumentException(errMsg);
325:
326: case 9:
327: if (isOKTokenChar(s[i])) {
328: if (i == len) {
329: paramValue = new String(s, i, 1);
330: addParameter(paramName, paramValue, nameVector,
331: valueVector);
332: } else {
333: startParamValueIndex = i;
334: state = 11;
335: }
336: break;
337: }
338: if (i == len) {
339: throw new IllegalArgumentException(errMsg);
340: }
341: if (isSpaceChar(s[i])) {
342: break;
343: }
344: if (s[i] == '(') {
345: state = 50;
346: nextState = 9;
347: break;
348: }
349: if (s[i] == '"') {
350: startParamValueIndex = i + 1;
351: state = 13;
352: break;
353: }
354: throw new IllegalArgumentException(errMsg);
355:
356: case 10:
357: if (i == len) {
358: throw new IllegalArgumentException(errMsg);
359: }
360: if (isSpaceChar(s[i])) {
361: break;
362: }
363: if (s[i] == '=') {
364: state = 9;
365: break;
366: }
367: throw new IllegalArgumentException(errMsg);
368:
369: case 11:
370: if (isOKTokenChar(s[i])) {
371: if (i == len) {
372: paramValue = new String(s,
373: startParamValueIndex, i + 1
374: - startParamValueIndex);
375: addParameter(paramName, paramValue, nameVector,
376: valueVector);
377: }
378: break;
379: }
380: if ((s[i] == ';') && (i != len)) {
381: paramValue = new String(s, startParamValueIndex, i
382: - startParamValueIndex);
383: addParameter(paramName, paramValue, nameVector,
384: valueVector);
385: state = 7;
386: break;
387: }
388: if (isSpaceChar(s[i])) {
389: paramValue = new String(s, startParamValueIndex, i
390: - startParamValueIndex);
391: addParameter(paramName, paramValue, nameVector,
392: valueVector);
393: state = 12;
394: break;
395: }
396: if ((s[i] == '(') && (i != len)) {
397: paramValue = new String(s, startParamValueIndex, i
398: - startParamValueIndex);
399: addParameter(paramName, paramValue, nameVector,
400: valueVector);
401: state = 50;
402: nextState = 12;
403: break;
404: }
405: throw new IllegalArgumentException(errMsg);
406:
407: case 12:
408: if (isSpaceChar(s[i])) {
409: break;
410: }
411: if ((s[i] == '(') && (i != len)) {
412: state = 50;
413: nextState = 12;
414: break;
415: }
416: if ((s[i] == ';') && (i != len)) {
417: state = 7;
418: break;
419: }
420: throw new IllegalArgumentException(errMsg);
421:
422: case 13:
423: if ((s[i] == '"') && (s[i - 1] != '\\')) {
424: paramValue = new String(s, startParamValueIndex, i
425: - startParamValueIndex);
426: addParameter(paramName, paramValue, nameVector,
427: valueVector);
428: state = 14;
429: break;
430: }
431: if (i == len) {
432: throw new IllegalArgumentException(errMsg);
433: }
434: if (isPrintableASCIIChar(s[i])) {
435: break;
436: }
437: throw new IllegalArgumentException(errMsg);
438:
439: case 14:
440: if ((s[i] == ';') && (i != len)) {
441: state = 7;
442: break;
443: }
444: if (isSpaceChar(s[i])) {
445: break;
446: }
447: if ((s[i] == '(') && (i != len)) {
448: state = 50;
449: nextState = 14;
450: break;
451: }
452: throw new IllegalArgumentException(errMsg);
453:
454: case 50:
455: if (s[i] == '(') {
456: cnt1++;
457: }
458: if (s[i] == ')') {
459: cnt1--;
460: }
461: if (cnt1 == 0) {
462: cnt1 = 1;
463: state = nextState;
464: break;
465: }
466: if (i == len) {
467: throw new IllegalArgumentException(errMsg);
468: }
469: if (isPrintableASCIIChar(s[i])) {
470: break;
471: }
472: throw new IllegalArgumentException(errMsg);
473: }
474: }
475: makeParamsArray(nameVector, valueVector);
476: }
477:
478: /*
479: * make parameters string array from name and value vectors
480: */
481: private void makeParamsArray(Vector nameVector, Vector valueVector) {
482: int[] arr = new int[nameVector.size()];
483: int tmp;
484:
485: // Sort parameter names
486: for (int i = 0; i < arr.length; i++) {
487: arr[i] = i;
488: }
489:
490: for (int i = 0; i < arr.length - 1; i++) {
491: for (int j = i + 1; j < arr.length; j++) {
492: if (((String) nameVector.get(arr[j]))
493: .compareTo((String) nameVector.get(arr[i])) <= 0) {
494: tmp = arr[i];
495: arr[i] = arr[j];
496: arr[j] = tmp;
497: }
498: }
499: }
500:
501: // copy vector values to the string array
502: params = new String[arr.length][2];
503: for (int i = 0; i < arr.length; i++) {
504: params[i][0] = (String) (nameVector.get(arr[i]));
505: params[i][1] = (String) (valueVector.get(arr[i]));
506: }
507: }
508:
509: /*
510: * add parameter name and value to the vectors.
511: */
512: private void addParameter(String name, String value,
513: Vector nameVector, Vector valueVector) {
514: // parameter names should not be duplicated
515: if (nameVector.contains(name)) {
516: throw new IllegalArgumentException(
517: "Duplicated parameters in mime type");
518: }
519:
520: nameVector.add(name);
521:
522: // charset parameter should be converted to lower case
523: if (name.equals(new String("charset"))) {
524: valueVector.add(value.toLowerCase());
525: } else {
526: valueVector.add(value);
527: }
528: }
529:
530: /*
531: * take part of the string and convert it to lower case (for types, subtypes
532: * and parameter names)
533: */
534: private String newLowercaseString(char[] arr, int start, int end) {
535: String s = new String(arr, start, end - start);
536: return s.toLowerCase();
537: }
538:
539: /*
540: * returns true if c is a special character which can not be contained in
541: * mime-type tokens. Such characters must be in quoted-string to use within
542: * parameter value
543: */
544: private boolean isSpecialChar(char c) {
545: return (c == '(') || (c == ')') || (c == '<') || (c == '>')
546: || (c == '@') || (c == ',') || (c == ';') || (c == ':')
547: || (c == '\\') || (c == '"') || (c == '/')
548: || (c == '[') || (c == ']') || (c == '?') || (c == '=');
549: }
550:
551: /*
552: * returns true if c is a line space character - space or tab
553: */
554: private boolean isSpaceChar(char c) {
555: return (c == ' ') || (c == 9);
556: }
557:
558: /*
559: * returns true if mime-type token can contain character c. Must be
560: * printable ASCII character, not separator and not special character
561: */
562: private boolean isOKTokenChar(char c) {
563: return (c < 127) && (c > 31) && (!isSpaceChar(c))
564: && (!isSpecialChar(c));
565: }
566:
567: /*
568: * returns true if c is a printable ASCII character
569: */
570: private boolean isPrintableASCIIChar(char c) {
571: // return (c<127) && (c>31);
572: return (c < 128);
573: }
574:
575: } /* End of MimeType class */
|