1 11 package org.eclipse.core.internal.content; 12 13 import java.io.*; 14 import java.util.*; 15 import org.eclipse.core.internal.runtime.RuntimeLog; 16 import org.eclipse.core.runtime.*; 17 import org.eclipse.core.runtime.content.*; 18 import org.eclipse.core.runtime.preferences.IScopeContext; 19 import org.eclipse.osgi.util.NLS; 20 import org.osgi.service.prefs.BackingStoreException; 21 import org.osgi.service.prefs.Preferences; 22 23 26 public final class ContentType implements IContentType, IContentTypeInfo { 27 28 29 private class InvalidDescriber implements IContentDescriber, ITextContentDescriber { 30 public int describe(InputStream contents, IContentDescription description) { 31 return INVALID; 32 } 33 34 public int describe(Reader contents, IContentDescription description) { 35 return INVALID; 36 } 37 38 public QualifiedName[] getSupportedOptions() { 39 return new QualifiedName[0]; 40 } 41 } 42 43 final static byte ASSOCIATED_BY_EXTENSION = 2; 44 final static byte ASSOCIATED_BY_NAME = 1; 45 private static final String DESCRIBER_ELEMENT = "describer"; private static ArrayList EMPTY_LIST = new ArrayList(0); 47 private static final Object INHERITED_DESCRIBER = "INHERITED DESCRIBER"; 49 private static final Object NO_DESCRIBER = "NO DESCRIBER"; final static byte NOT_ASSOCIATED = 0; 51 public final static String PREF_DEFAULT_CHARSET = "charset"; public final static String PREF_FILE_EXTENSIONS = "file-extensions"; public final static String PREF_FILE_NAMES = "file-names"; final static byte PRIORITY_HIGH = 1; 55 final static byte PRIORITY_LOW = -1; 56 final static byte PRIORITY_NORMAL = 0; 57 final static int SPEC_PRE_DEFINED = IGNORE_PRE_DEFINED; 58 final static int SPEC_USER_DEFINED = IGNORE_USER_DEFINED; 59 final static byte STATUS_INVALID = 2; 60 final static byte STATUS_UNKNOWN = 0; 61 final static byte STATUS_VALID = 1; 62 private String aliasTargetId; 63 private String baseTypeId; 64 private boolean builtInAssociations = false; 65 private ContentTypeCatalog catalog; 66 private IConfigurationElement contentTypeElement; 67 private DefaultDescription defaultDescription; 68 private Map defaultProperties; 69 private Object describer; 70 private ArrayList fileSpecs = EMPTY_LIST; 72 String id; 73 private ContentTypeManager manager; 74 private String name; 75 private byte priority; 76 private ContentType target; 77 private String userCharset; 78 private byte validation = STATUS_UNKNOWN; 79 private ContentType baseType; 80 private byte depth = -1; 82 83 public static ContentType createContentType(ContentTypeCatalog catalog, String uniqueId, String name, byte priority, String [] fileExtensions, String [] fileNames, String baseTypeId, String aliasTargetId, Map defaultProperties, IConfigurationElement contentTypeElement) { 84 ContentType contentType = new ContentType(catalog.getManager()); 85 contentType.catalog = catalog; 86 contentType.defaultDescription = new DefaultDescription(contentType); 87 contentType.id = uniqueId; 88 contentType.name = name; 89 contentType.priority = priority; 90 if ((fileExtensions != null && fileExtensions.length > 0) || (fileNames != null && fileNames.length > 0)) { 91 contentType.builtInAssociations = true; 92 contentType.fileSpecs = new ArrayList(fileExtensions.length + fileNames.length); 93 for (int i = 0; i < fileNames.length; i++) 94 contentType.internalAddFileSpec(fileNames[i], FILE_NAME_SPEC | SPEC_PRE_DEFINED); 95 for (int i = 0; i < fileExtensions.length; i++) 96 contentType.internalAddFileSpec(fileExtensions[i], FILE_EXTENSION_SPEC | SPEC_PRE_DEFINED); 97 } 98 contentType.defaultProperties = defaultProperties; 99 contentType.contentTypeElement = contentTypeElement; 100 contentType.baseTypeId = baseTypeId; 101 contentType.aliasTargetId = aliasTargetId; 102 return contentType; 103 } 104 105 static FileSpec createFileSpec(String fileSpec, int type) { 106 return new FileSpec(fileSpec, type); 107 } 108 109 static String getPreferenceKey(int flags) { 110 if ((flags & FILE_EXTENSION_SPEC) != 0) 111 return PREF_FILE_EXTENSIONS; 112 if ((flags & FILE_NAME_SPEC) != 0) 113 return PREF_FILE_NAMES; 114 throw new IllegalArgumentException ("Unknown type: " + flags); } 116 117 private static String getValidationString(byte validation) { 118 return validation == STATUS_VALID ? "VALID" : (validation == STATUS_INVALID ? "INVALID" : "UNKNOWN"); } 120 121 public static void log(String message, Throwable reason) { 122 IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, reason instanceof CoreException ? null : reason); 124 RuntimeLog.log(status); 125 } 126 127 public ContentType(ContentTypeManager manager) { 128 this.manager = manager; 129 } 130 131 134 public void addFileSpec(String fileSpec, int type) throws CoreException { 135 Assert.isLegal(type == FILE_EXTENSION_SPEC || type == FILE_NAME_SPEC, "Unknown type: " + type); String [] userSet; 137 synchronized (this) { 138 if (!internalAddFileSpec(fileSpec, type | SPEC_USER_DEFINED)) 139 return; 140 userSet = getFileSpecs(type | IGNORE_PRE_DEFINED); 141 } 142 Preferences contentTypeNode = manager.getPreferences().node(id); 144 String newValue = Util.toListString(userSet); 145 Assert.isNotNull(newValue); 147 setPreference(contentTypeNode, getPreferenceKey(type), newValue); 148 try { 149 contentTypeNode.flush(); 150 } catch (BackingStoreException bse) { 151 String message = NLS.bind(ContentMessages.content_errorSavingSettings, id); 152 IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, bse); 153 throw new CoreException(status); 154 } 155 manager.fireContentTypeChangeEvent(this); 157 } 158 159 int describe(IContentDescriber selectedDescriber, ILazySource contents, ContentDescription description) throws IOException { 160 try { 161 return contents.isText() ? ((ITextContentDescriber) selectedDescriber).describe((Reader) contents, description) : selectedDescriber.describe((InputStream) contents, description); 162 } catch (RuntimeException re) { 163 invalidateDescriber(re); 165 } catch (Error e) { 166 invalidateDescriber(e); 168 throw e; 169 } catch (LowLevelIOException llioe) { 170 throw llioe.getActualException(); 172 } catch (IOException ioe) { 173 if (ContentTypeManager.DEBUGGING) { 175 String message = NLS.bind(ContentMessages.content_errorReadingContents, id); 176 ContentType.log(message, ioe); 177 } 178 return IContentDescriber.INDETERMINATE; 180 } finally { 181 contents.rewind(); 182 } 183 return IContentDescriber.INVALID; 184 } 185 186 public boolean equals(Object another) { 187 if (another instanceof ContentType) 188 return id.equals(((ContentType) another).id); 189 if (another instanceof ContentTypeHandler) 190 return id.equals(((ContentTypeHandler) another).id); 191 return false; 192 } 193 194 public String getAliasTargetId() { 195 return aliasTargetId; 196 } 197 198 201 202 public IContentType getBaseType() { 203 return baseType; 204 } 205 206 String getBaseTypeId() { 207 return baseTypeId; 208 } 209 210 public ContentTypeCatalog getCatalog() { 211 return catalog; 212 } 213 214 public ContentType getContentType() { 215 return this; 216 } 217 218 221 public String getDefaultCharset() { 222 return getDefaultProperty(IContentDescription.CHARSET); 223 } 224 225 228 public IContentDescription getDefaultDescription() { 229 return defaultDescription; 230 } 231 232 235 public String getDefaultProperty(QualifiedName key) { 236 String propertyValue = internalGetDefaultProperty(key); 237 if ("".equals(propertyValue)) return null; 239 return propertyValue; 240 } 241 242 byte getDepth() { 243 byte tmpDepth = depth; 244 if (tmpDepth >= 0) 245 return tmpDepth; 246 if (baseType == null) 248 return depth = 0; 249 return depth = (byte) (baseType == null ? 0 : (1 + baseType.getDepth())); 250 } 251 252 255 public IContentDescriber getDescriber() { 256 try { 257 Object tmpDescriber = describer; 259 if (tmpDescriber != null) { 260 if (INHERITED_DESCRIBER == tmpDescriber) 261 return baseType.getDescriber(); 262 return (NO_DESCRIBER == tmpDescriber) ? null : (IContentDescriber) tmpDescriber; 263 } 264 final String describerValue = contentTypeElement.getAttributeAsIs(DESCRIBER_ELEMENT); 265 if (describerValue != null || contentTypeElement.getChildren(DESCRIBER_ELEMENT).length > 0) 266 try { 267 if ("".equals(describerValue)) { describer = NO_DESCRIBER; 269 return null; 270 } 271 describer = tmpDescriber = contentTypeElement.createExecutableExtension(DESCRIBER_ELEMENT); 272 return (IContentDescriber) tmpDescriber; 273 } catch (CoreException ce) { 274 return invalidateDescriber(ce); 278 } 279 } catch (InvalidRegistryObjectException e) { 280 284 manager.invalidate(); 286 return null; 288 } 289 if (baseType == null) { 290 describer = NO_DESCRIBER; 291 return null; 292 } 293 describer = INHERITED_DESCRIBER; 295 return baseType.getDescriber(); 296 } 297 298 301 public IContentDescription getDescriptionFor(InputStream contents, QualifiedName[] options) throws IOException { 302 return internalGetDescriptionFor(ContentTypeManager.readBuffer(contents), options); 303 } 304 305 308 public IContentDescription getDescriptionFor(Reader contents, QualifiedName[] options) throws IOException { 309 return internalGetDescriptionFor(ContentTypeManager.readBuffer(contents), options); 310 } 311 312 315 public String [] getFileSpecs(int typeMask) { 316 if (fileSpecs.isEmpty()) 317 return new String [0]; 318 typeMask ^= (IGNORE_PRE_DEFINED | IGNORE_USER_DEFINED); 320 List result = new ArrayList(fileSpecs.size()); 321 for (Iterator i = fileSpecs.iterator(); i.hasNext();) { 322 FileSpec spec = (FileSpec) i.next(); 323 if ((spec.getType() & typeMask) == spec.getType()) 324 result.add(spec.getText()); 325 } 326 return (String []) result.toArray(new String [result.size()]); 327 } 328 329 332 public String getId() { 333 return id; 334 } 335 336 339 public String getName() { 340 return name; 341 } 342 343 byte getPriority() { 344 return priority; 345 } 346 347 public IContentTypeSettings getSettings(IScopeContext context) { 348 if (context == null || context.equals(manager.getContext())) 349 return this; 350 return new ContentTypeSettings(this, context); 351 } 352 353 356 ContentType getAliasTarget(boolean self) { 357 return (self && target == null) ? this : target; 358 } 359 360 byte getValidation() { 361 return validation; 362 } 363 364 boolean hasBuiltInAssociations() { 365 return builtInAssociations; 366 } 367 368 boolean hasFileSpec(IScopeContext context, String text, int typeMask) { 369 if (context.equals(manager.getContext()) || (typeMask & IGNORE_USER_DEFINED) != 0) 370 return hasFileSpec(text, typeMask, false); 371 String [] fileSpecs = ContentTypeSettings.getFileSpecs(context, id, typeMask); 372 for (int i = 0; i < fileSpecs.length; i++) 373 if (text.equalsIgnoreCase(fileSpecs[i])) 374 return true; 375 return hasFileSpec(text, typeMask | IGNORE_PRE_DEFINED, false); 377 } 378 379 387 boolean hasFileSpec(String text, int typeMask, boolean strict) { 388 if (fileSpecs.isEmpty()) 389 return false; 390 for (Iterator i = fileSpecs.iterator(); i.hasNext();) { 391 FileSpec spec = (FileSpec) i.next(); 392 if (spec.equals(text, typeMask, strict)) 393 return true; 394 } 395 return false; 396 } 397 398 public int hashCode() { 399 return id.hashCode(); 400 } 401 402 405 boolean internalAddFileSpec(String fileSpec, int typeMask) { 406 if (hasFileSpec(fileSpec, typeMask, false)) 407 return false; 408 FileSpec newFileSpec = createFileSpec(fileSpec, typeMask); 409 if ((typeMask & ContentType.SPEC_USER_DEFINED) == 0) { 410 if (fileSpecs.isEmpty()) 412 fileSpecs = new ArrayList(3); 413 fileSpecs.add(newFileSpec); 414 return true; 415 } 416 ArrayList tmpFileSpecs = (ArrayList) fileSpecs.clone(); 418 tmpFileSpecs.add(newFileSpec); 419 catalog.associate(this, newFileSpec.getText(), newFileSpec.getType()); 420 fileSpecs = tmpFileSpecs; 422 return true; 423 } 424 425 428 String internalGetDefaultProperty(QualifiedName key) { 429 if (userCharset != null && key.equals(IContentDescription.CHARSET)) 431 return userCharset; 432 String defaultValue = basicGetDefaultProperty(key); 433 if (defaultValue != null) 434 return defaultValue; 435 return baseType == null ? null : baseType.internalGetDefaultProperty(key); 437 } 438 439 442 String basicGetDefaultProperty(QualifiedName key) { 443 return defaultProperties == null ? null : (String ) defaultProperties.get(key); 444 } 445 446 BasicDescription internalGetDescriptionFor(ILazySource buffer, QualifiedName[] options) throws IOException { 447 if (buffer == null) 448 return defaultDescription; 449 IContentDescriber tmpDescriber = this.getDescriber(); 451 if (tmpDescriber == null) 453 return defaultDescription; 454 if (buffer.isText() && !(tmpDescriber instanceof ITextContentDescriber)) 455 throw new UnsupportedOperationException (); 457 ContentDescription description = new ContentDescription(options, this); 458 if (describe(tmpDescriber, buffer, description) == IContentDescriber.INVALID) 459 return null; 461 if (!description.isSet()) 463 return defaultDescription; 464 description.markImmutable(); 466 return description; 467 } 468 469 byte internalIsAssociatedWith(String fileName, IScopeContext context) { 470 if (hasFileSpec(context, fileName, FILE_NAME_SPEC)) 471 return ASSOCIATED_BY_NAME; 472 String fileExtension = ContentTypeManager.getFileExtension(fileName); 473 if (hasFileSpec(context, fileExtension, FILE_EXTENSION_SPEC)) 474 return ASSOCIATED_BY_EXTENSION; 475 if (!hasBuiltInAssociations() && baseType != null) 477 return baseType.internalIsAssociatedWith(fileName, context); 478 return NOT_ASSOCIATED; 479 } 480 481 boolean internalRemoveFileSpec(String fileSpec, int typeMask) { 482 if (fileSpecs.isEmpty()) 483 return false; 484 ArrayList tmpFileSpecs = (ArrayList) fileSpecs.clone(); 486 for (Iterator i = tmpFileSpecs.iterator(); i.hasNext();) { 487 FileSpec spec = (FileSpec) i.next(); 488 if ((spec.getType() == typeMask) && fileSpec.equals(spec.getText())) { 489 i.remove(); 490 catalog.dissociate(this, spec.getText(), spec.getType()); 491 fileSpecs = tmpFileSpecs; 493 return true; 494 } 495 } 496 return false; 497 } 498 499 private IContentDescriber invalidateDescriber(Throwable reason) { 500 String message = NLS.bind(ContentMessages.content_invalidContentDescriber, id); 501 log(message, reason); 502 return (IContentDescriber) (describer = new InvalidDescriber()); 503 } 504 505 boolean isAlias() { 506 return target != null; 507 } 508 509 512 public boolean isAssociatedWith(String fileName) { 513 return isAssociatedWith(fileName, manager.getContext()); 514 } 515 516 519 public boolean isAssociatedWith(String fileName, IScopeContext context) { 520 return internalIsAssociatedWith(fileName, context) != NOT_ASSOCIATED; 521 } 522 523 526 public boolean isKindOf(IContentType another) { 527 if (another == null) 528 return false; 529 if (this == another) 530 return true; 531 return baseType != null && baseType.isKindOf(another); 532 } 533 534 boolean isValid() { 535 return validation == STATUS_VALID; 536 } 537 538 void processPreferences(Preferences contentTypeNode) { 539 this.userCharset = contentTypeNode.get(PREF_DEFAULT_CHARSET, null); 541 String userSetFileNames = contentTypeNode.get(PREF_FILE_NAMES, null); 543 String [] fileNames = Util.parseItems(userSetFileNames); 544 for (int i = 0; i < fileNames.length; i++) 545 internalAddFileSpec(fileNames[i], FILE_NAME_SPEC | SPEC_USER_DEFINED); 546 String userSetFileExtensions = contentTypeNode.get(PREF_FILE_EXTENSIONS, null); 548 String [] fileExtensions = Util.parseItems(userSetFileExtensions); 549 for (int i = 0; i < fileExtensions.length; i++) 550 internalAddFileSpec(fileExtensions[i], FILE_EXTENSION_SPEC | SPEC_USER_DEFINED); 551 } 552 553 556 public void removeFileSpec(String fileSpec, int type) throws CoreException { 557 Assert.isLegal(type == FILE_EXTENSION_SPEC || type == FILE_NAME_SPEC, "Unknown type: " + type); synchronized (this) { 559 if (!internalRemoveFileSpec(fileSpec, type | SPEC_USER_DEFINED)) 560 return; 561 } 562 Preferences contentTypeNode = manager.getPreferences().node(id); 564 final String [] userSet = getFileSpecs(type | IGNORE_PRE_DEFINED); 565 String preferenceKey = getPreferenceKey(type); 566 String newValue = Util.toListString(userSet); 567 setPreference(contentTypeNode, preferenceKey, newValue); 568 try { 569 contentTypeNode.flush(); 570 } catch (BackingStoreException bse) { 571 String message = NLS.bind(ContentMessages.content_errorSavingSettings, id); 572 IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, bse); 573 throw new CoreException(status); 574 } 575 manager.fireContentTypeChangeEvent(this); 577 } 578 579 void setAliasTarget(ContentType newTarget) { 580 target = newTarget; 581 } 582 583 586 public void setDefaultCharset(String newCharset) throws CoreException { 587 synchronized (this) { 588 if (userCharset == null) { 590 if (newCharset == null) 591 return; 592 } else if (userCharset.equals(newCharset)) 593 return; 594 userCharset = newCharset; 596 } 597 Preferences contentTypeNode = manager.getPreferences().node(id); 599 setPreference(contentTypeNode, PREF_DEFAULT_CHARSET, userCharset); 600 try { 601 contentTypeNode.flush(); 602 } catch (BackingStoreException bse) { 603 String message = NLS.bind(ContentMessages.content_errorSavingSettings, id); 604 IStatus status = new Status(IStatus.ERROR, ContentMessages.OWNER_NAME, 0, message, bse); 605 throw new CoreException(status); 606 } 607 manager.fireContentTypeChangeEvent(this); 609 } 610 611 static void setPreference(Preferences node, String key, String value) { 612 if (value == null) 613 node.remove(key); 614 else 615 node.put(key, value); 616 } 617 618 void setValidation(byte validation) { 619 this.validation = validation; 620 if (ContentTypeManager.DEBUGGING) 621 ContentMessages.message("Validating " + this + ": " + getValidationString(validation)); } 623 624 public String toString() { 625 return id; 626 } 627 628 void setBaseType(ContentType baseType) { 629 this.baseType = baseType; 630 } 631 632 } 633 | Popular Tags |