1 19 20 package org.netbeans.core.filesystems; 21 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.util.logging.Level ; 25 import java.util.logging.Logger ; 26 import org.openide.cookies.InstanceCookie; 27 import org.openide.filesystems.FileObject; 28 import org.openide.filesystems.FileUtil; 29 import org.openide.filesystems.MIMEResolver; 30 import org.openide.loaders.DataObject; 31 import org.openide.loaders.Environment; 32 import org.openide.util.Utilities; 33 import org.openide.util.lookup.InstanceContent; 34 import org.openide.xml.XMLUtil; 35 import org.xml.sax.Attributes ; 36 import org.xml.sax.SAXException ; 37 38 55 public final class MIMEResolverImpl extends XMLEnvironmentProvider implements Environment.Provider { 56 57 private static final long serialVersionUID = 18975L; 58 59 private static final Logger ERR = Logger.getLogger(MIMEResolverImpl.class.getName()); 61 62 private static final boolean CASE_INSENSITIVE = 63 Utilities.isWindows() || Utilities.getOperatingSystem() == Utilities.OS_VMS; 64 65 67 protected InstanceContent createInstanceContent(DataObject obj) { 68 FileObject fo = obj.getPrimaryFile(); 69 InstanceContent ic = new InstanceContent(); 70 ic.add(new Impl(fo)); 71 return ic; 72 } 73 74 76 static class Impl extends MIMEResolver implements InstanceCookie { 81 private final FileObject data; 83 84 private FileElement[] smell = null; 86 87 private short state = DescParser.INIT; 88 89 Impl(FileObject obj) { 90 if (ERR.isLoggable(Level.FINE)) ERR.fine("MIMEResolverImpl.Impl.<init>(" + obj + ")"); data = obj; 92 } 93 94 99 public String findMIMEType(FileObject fo) { 100 101 synchronized (this) { 103 if (state == DescParser.INIT) { 104 state = parseDesc(); 105 } 106 107 if (state == DescParser.ERROR) { 108 return null; 109 } 110 } 111 112 114 for (int i = smell.length-1; i>=0; i--) { 115 String s = smell[i].resolve(fo); 116 if (s != null) { 117 if (ERR.isLoggable(Level.FINE)) ERR.fine("MIMEResolverImpl.findMIMEType(" + fo + ")=" + s); return s; 119 } 120 } 121 122 return null; 123 } 124 125 private short parseDesc() { 127 smell = new FileElement[0]; 128 DescParser parser = new DescParser(data); 129 parser.parse(); 130 smell = (parser.template != null) ? parser.template : smell; 131 if (ERR.isLoggable(Level.FINE)) { 132 if (parser.state == DescParser.ERROR) { 133 ERR.fine("MIMEResolverImpl.Impl parsing error!"); 134 } else { 135 StringBuffer buf = new StringBuffer (); 136 buf.append("Parse: "); 137 for (int i = 0; i<smell.length; i++) 138 buf.append('\n').append(smell[i]); 139 ERR.fine(buf.toString()); 140 } 141 } 142 return parser.state; 143 } 144 145 147 public Object instanceCreate() { 148 return this; 149 } 150 151 public Class instanceClass() { 152 return this.getClass(); 153 } 154 155 public String instanceName() { 156 return this.getClass().getName(); 157 } 158 159 160 public String toString() { 161 return "MIMEResolverImpl.Impl[" + data + ", " + smell + "]"; } 163 164 165 } 166 167 168 170 174 private static class DescParser extends DefaultParser { 175 176 private FileElement[] template = null; 177 178 private short file_state = INIT; 180 181 private MIMEComponent component = null; 183 private String componentDelimiter = null; 184 185 186 DescParser(FileObject fo) { 187 super(fo); 188 } 189 190 private static final short IN_ROOT = 1; 192 private static final short IN_FILE = 2; 193 private static final short IN_RESOLVER = 3; 194 private static final short IN_COMPONENT = 4; 195 196 private static final short IN_EXIT = INIT + 1; 198 199 private static final String ROOT = "MIME-resolver"; private static final String FILE = "file"; private static final String MIME = "mime"; private static final String EXT = "ext"; private static final String RESOLVER = "resolver"; private static final String FATTR = "fattr"; private static final String NAME = "name"; private static final String MAGIC = "magic"; private static final String HEX = "hex"; private static final String MASK = "mask"; private static final String VALUE = "text"; private static final String EXIT = "exit"; private static final String XML_RULE_COMPONENT = "xml-rule"; 214 public void startElement(String namespaceURI, String localName, String qName, Attributes atts) throws SAXException { 215 216 String s; 217 218 switch (state) { 219 220 case INIT: 221 222 if (ROOT.equals(qName) == false) error(); 223 state = IN_ROOT; 224 break; 225 226 case IN_ROOT: 227 if (FILE.equals(qName) == false) error(); 228 229 232 if (template == null) { 233 template = new FileElement[] {new FileElement()}; 234 } else { 235 FileElement[] n = new FileElement[template.length +1]; 236 System.arraycopy(template, 0, n, 1, template.length); 237 n[0] = new FileElement(); 238 template = n; 239 } 240 241 state = IN_FILE; 242 break; 243 244 case IN_FILE: 245 246 if (file_state == IN_EXIT) error(); 247 248 if (EXT.equals(qName)) { 249 250 s = atts.getValue(NAME); if (s == null) error(); 251 template[0].fileCheck.addExt(s); 252 253 } else if (MAGIC.equals(qName)) { 254 255 s = atts.getValue(HEX); if (s == null) error(); 256 String mask = atts.getValue(MASK); 257 258 char[] chars = s.toCharArray(); 259 byte[] mask_bytes = null; 261 try { 262 263 if (mask != null) { 264 char[] mask_chars = mask.toCharArray(); 265 mask_bytes = XMLUtil.fromHex(mask_chars, 0, mask_chars.length); 266 } 267 268 byte[] magic = XMLUtil.fromHex(chars, 0, chars.length); 269 if (template[0].fileCheck.setMagic(magic, mask_bytes) == false) { 270 error(); 271 } 272 } catch (IOException ioex) { 273 error(); 274 } 275 276 277 } else if (MIME.equals(qName)) { 278 279 s = atts.getValue(NAME); if (s == null) error(); 280 template[0].fileCheck.addMIME(s); 281 282 } else if (FATTR.equals(qName)) { 283 284 s = atts.getValue(NAME); if (s == null) error(); 285 String val = atts.getValue(VALUE); 286 template[0].fileCheck.addAttr(s, val); 287 288 } else if (RESOLVER.equals(qName)) { 289 290 if (template[0].fileCheck.exts == null 291 && template[0].fileCheck.mimes == null 292 && template[0].fileCheck.fatts == null 293 && template[0].fileCheck.magic == null) { 294 error(); } 296 297 s = atts.getValue(MIME); if (s == null) error(); 298 template[0].setMIME(s); 299 300 state = IN_RESOLVER; 301 302 break; 303 304 } else if (EXIT.equals(qName)) { 305 306 file_state = IN_EXIT; 307 break; 308 309 310 } else { 311 String reason = "Unexpected element: " + qName; 312 error(reason); 313 } 314 break; 315 316 case IN_RESOLVER: 317 318 321 324 if (XML_RULE_COMPONENT.equals(qName)) { 325 enterComponent(XML_RULE_COMPONENT, new XMLMIMEComponent()); 326 component.startElement(namespaceURI, localName, qName, atts); 327 } 328 329 break; 330 331 case IN_COMPONENT: 332 333 component.startElement(namespaceURI, localName, qName, atts); 334 break; 335 336 default: 337 338 } 339 } 340 341 private void enterComponent(String name, MIMEComponent component) { 342 this.component = component; 343 componentDelimiter = name; 344 345 component.setDocumentLocator(getLocator()); 346 template[0].rule = component; 347 state = IN_COMPONENT; 348 } 349 350 public void endElement(String namespaceURI, String localName, String qName) throws SAXException { 351 switch (state) { 352 case IN_FILE: 353 if (FILE.equals(qName)) { 354 state = IN_ROOT; 355 file_state = INIT; 356 } 357 break; 358 359 case IN_RESOLVER: 360 if (RESOLVER.equals(qName)) { 361 state = IN_FILE; 362 } 363 break; 364 365 case IN_COMPONENT: 366 component.endElement(namespaceURI, localName, qName); 367 if (componentDelimiter.equals(qName)) { 368 state = IN_RESOLVER; 369 } 370 break; 371 } 372 } 373 374 public void characters(char[] data, int offset, int len) throws SAXException { 375 if (state == IN_COMPONENT) component.characters(data, offset, len); 376 } 377 } 378 379 385 private static class FileElement { 386 FileElement() {} 387 388 private Type fileCheck = new Type(); 389 private String mime = null; 390 private MIMEComponent rule = null; 391 392 private void setMIME(String mime) { 393 if ("null".equals(mime)) return; this.mime = mime; 395 } 396 397 private String resolve(FileObject file) { 398 399 try { 400 if (fileCheck.accept(file)) { 401 if (mime == null) return null; 402 if (rule == null) return mime; 403 if (rule.acceptFileObject(file)) return mime; 404 } 405 } catch (IOException io) { 406 Logger.getLogger(MIMEResolverImpl.class.getName()).log(Level.WARNING, null, io); 407 } 408 return null; 409 } 410 411 414 public String toString() { 415 StringBuffer buf = new StringBuffer (); 416 buf.append("FileElement("); 417 buf.append(fileCheck).append(' '); 418 buf.append(rule).append(' '); 419 buf.append("Result:").append(mime); 420 return buf.toString(); 421 } 422 } 423 424 425 433 private static class Type { 434 Type() {} 435 private String [] exts; 436 private String [] mimes; 437 private String [] fatts; 438 private String [] vals; private byte[] magic; 440 private byte[] mask; 441 442 443 446 public String toString() { 447 int i = 0; 448 StringBuffer buf = new StringBuffer (); 449 450 buf.append("fast-check("); 451 452 if (exts != null) { 453 buf.append("exts:"); 454 for (i = 0; i<exts.length; i++) 455 buf.append(exts[i]).append(", "); 456 } 457 458 if (mimes != null) { 459 buf.append("mimes:"); 460 for (i = 0; i<mimes.length; i++) 461 buf.append(mimes[i]).append(", "); 462 } 463 464 if (fatts != null) { 465 buf.append("file-attributes:"); 466 for (i = 0; i<fatts.length; i++) 467 buf.append(fatts[i]).append("='").append(vals[i]).append("', "); 468 } 469 470 if (magic != null) { 471 buf.append("magic:").append(XMLUtil.toHex(magic, 0, magic.length)); 472 } 473 474 if (mask != null) { 475 buf.append("mask:").append(XMLUtil.toHex(mask, 0, mask.length)); 476 } 477 478 buf.append(')'); 479 480 return buf.toString(); 481 } 482 483 private void addExt(String ext) { 484 exts = Util.addString(exts, ext); 485 } 486 487 private void addMIME(String mime) { 488 mimes = Util.addString(mimes, mime.toLowerCase()); 489 } 490 491 private void addAttr(String name, String value) { 492 fatts = Util.addString(fatts, name); 493 vals = Util.addString(vals, value); 494 } 495 496 private boolean setMagic(byte[] magic, byte[] mask) { 497 if (magic == null) return true; 498 if (mask != null && magic.length != mask.length) return false; 499 this.magic = magic; 500 if (mask != null) { 501 this.mask = mask; 502 for (int i = 0; i<mask.length; i++) { 503 this.magic[i] &= mask[i]; 504 } 505 } 506 return true; 507 } 508 509 private boolean accept(FileObject fo) throws IOException { 510 511 513 if (exts != null && fo.getExt() != null) { 514 if (Util.contains(exts, fo.getExt(), CASE_INSENSITIVE)) return true; 515 } 516 517 519 if (mimes != null) { 520 for (int i = mimes.length -1 ; i>=0; i--) { 521 String s = FileUtil.getMIMEType(fo.getExt()); if (s == null) continue; 523 524 526 int l = s.indexOf(';'); 527 if (l>=0) s = s.substring(0, l-1); 528 s = s.toLowerCase(); 529 if (s.equals(mimes[i])) return true; 530 531 533 if (mimes[i].length() > 0 && mimes[i].charAt(0) == '+' && s.endsWith(mimes[i])) return true; } 535 } 536 537 539 if (magic != null) { 540 byte[] header = new byte[magic.length]; 541 542 544 547 549 InputStream in = null; 550 boolean unexpectedEnd = false; 551 try { 552 in = fo.getInputStream(); 553 for (int i = 0; i<magic.length; ) { 554 try { 555 int read = in.read(header, i, magic.length-i); 556 if (read < 0) unexpectedEnd = true; 557 i += read; 558 } catch (IOException ex) { 559 unexpectedEnd = true; 560 break; 561 } 562 if (unexpectedEnd) break; 563 } 564 } catch (IOException openex) { 565 unexpectedEnd = true; 566 if (fo.canRead() == true) { 567 throw openex; 568 } else { 569 } 571 } finally { 572 try { 573 if (in != null) in.close(); 574 } catch (IOException ioe) { 575 } 577 } 578 579 580 582 584 if ( unexpectedEnd == false ) { 585 boolean diff = false; 586 for (int i=0 ; i<magic.length; i++) { 587 if (mask != null) header[i] &= mask[i]; 588 if (magic[i] != header[i]) { 589 diff = true; 590 break; 591 } 592 } 593 594 if (diff == false) return true; 595 } 596 } 597 598 600 if (fatts != null) { 601 for (int i = fatts.length -1 ; i>=0; i--) { 602 Object attr = fo.getAttribute(fatts[i]); 603 if (attr != null) { 604 if (vals[i] == null) return true; 605 if (vals[i].equals(attr.toString())) return true; 606 } 607 } 608 } 609 610 612 return false; 613 } 614 615 } 616 } 617 | Popular Tags |