1 11 package org.eclipse.core.internal.content; 12 13 import java.io.*; 14 import java.util.*; 15 import org.eclipse.core.runtime.*; 16 import org.eclipse.core.runtime.content.*; 17 import org.eclipse.core.runtime.content.IContentTypeManager.ISelectionPolicy; 18 import org.eclipse.core.runtime.preferences.IScopeContext; 19 20 public final class ContentTypeCatalog { 21 private static final IContentType[] NO_CONTENT_TYPES = new IContentType[0]; 22 23 private Map allChildren = new HashMap(); 24 private Map contentTypes = new HashMap(); 25 26 private Map fileExtensions = new HashMap(); 27 28 private Map fileNames = new HashMap(); 29 30 private int generation; 31 32 private ContentTypeManager manager; 33 34 38 private Comparator policyConstantGeneralIsBetter = new Comparator() { 39 public int compare(Object o1, Object o2) { 40 ContentType type1 = (ContentType) o1; 41 ContentType type2 = (ContentType) o2; 42 int depthCriteria = type1.getDepth() - type2.getDepth(); 44 if (depthCriteria != 0) 45 return depthCriteria; 46 int priorityCriteria = type1.getPriority() - type2.getPriority(); 48 if (priorityCriteria != 0) 49 return -priorityCriteria; 50 return type1.getId().compareTo(type2.getId()); 52 } 53 }; 54 55 59 private Comparator policyConstantSpecificIsBetter = new Comparator() { 60 public int compare(Object o1, Object o2) { 61 ContentType type1 = (ContentType) o1; 62 ContentType type2 = (ContentType) o2; 63 int depthCriteria = type1.getDepth() - type2.getDepth(); 65 if (depthCriteria != 0) 66 return -depthCriteria; 67 int priorityCriteria = type1.getPriority() - type2.getPriority(); 69 if (priorityCriteria != 0) 70 return -priorityCriteria; 71 return type1.getId().compareTo(type2.getId()); 73 } 74 }; 75 76 79 private Comparator policyGeneralIsBetter = new Comparator() { 80 public int compare(Object o1, Object o2) { 81 ContentType type1 = (ContentType) o1; 82 ContentType type2 = (ContentType) o2; 83 int depthCriteria = type1.getDepth() - type2.getDepth(); 85 if (depthCriteria != 0) 86 return depthCriteria; 87 int priorityCriteria = type1.getPriority() - type2.getPriority(); 89 if (priorityCriteria != 0) 90 return -priorityCriteria; 91 return 0; 92 } 93 }; 94 95 98 private Comparator policyLexicographical = new Comparator() { 99 public int compare(Object o1, Object o2) { 100 ContentType type1 = (ContentType) o1; 101 ContentType type2 = (ContentType) o2; 102 return type1.getId().compareTo(type2.getId()); 103 } 104 }; 105 108 private Comparator policySpecificIsBetter = new Comparator() { 109 public int compare(Object o1, Object o2) { 110 ContentType type1 = (ContentType) o1; 111 ContentType type2 = (ContentType) o2; 112 int depthCriteria = type1.getDepth() - type2.getDepth(); 114 if (depthCriteria != 0) 115 return -depthCriteria; 116 int priorityCriteria = type1.getPriority() - type2.getPriority(); 118 if (priorityCriteria != 0) 119 return -priorityCriteria; 120 return 0; 121 } 122 }; 123 124 private static IContentType[] concat(IContentType[][] types) { 125 if (types[0].length == 0) 126 return types[1]; 127 if (types[1].length == 0) 128 return types[0]; 129 IContentType[] result = new IContentType[types[0].length + types[1].length]; 130 System.arraycopy(types[0], 0, result, 0, types[0].length); 131 System.arraycopy(types[1], 0, result, types[0].length, types[1].length); 132 return result; 133 } 134 135 public ContentTypeCatalog(ContentTypeManager manager, int generation) { 136 this.manager = manager; 137 this.generation = generation; 138 } 139 140 void addContentType(IContentType contentType) { 141 contentTypes.put(contentType.getId(), contentType); 142 } 143 144 147 private IContentType[] applyPolicy(final IContentTypeManager.ISelectionPolicy policy, final IContentType[] candidates, final boolean fileName, final boolean contents) { 148 final IContentType[][] result = new IContentType[][] {candidates}; 149 SafeRunner.run(new ISafeRunnable() { 150 public void handleException(Throwable exception) { 151 } 155 156 public void run() throws Exception { 157 result[0] = policy.select(candidates, fileName, contents); 158 } 159 }); 160 return result[0]; 161 } 162 163 void associate(ContentType contentType) { 164 String [] builtInFileNames = contentType.getFileSpecs(IContentType.IGNORE_USER_DEFINED | IContentType.FILE_NAME_SPEC); 165 for (int i = 0; i < builtInFileNames.length; i++) 166 associate(contentType, builtInFileNames[i], IContentType.FILE_NAME_SPEC); 167 String [] builtInFileExtensions = contentType.getFileSpecs(IContentType.IGNORE_USER_DEFINED | IContentType.FILE_EXTENSION_SPEC); 168 for (int i = 0; i < builtInFileExtensions.length; i++) 169 associate(contentType, builtInFileExtensions[i], IContentType.FILE_EXTENSION_SPEC); 170 } 171 172 void associate(ContentType contentType, String text, int type) { 173 Map fileSpecMap = ((type & IContentType.FILE_NAME_SPEC) != 0) ? fileNames : fileExtensions; 174 String mappingKey = FileSpec.getMappingKeyFor(text); 175 Set existing = (Set) fileSpecMap.get(mappingKey); 176 if (existing == null) 177 fileSpecMap.put(mappingKey, existing = new HashSet()); 178 existing.add(contentType); 179 } 180 181 private int collectMatchingByContents(int valid, IContentType[] subset, List destination, ILazySource contents) throws IOException { 182 for (int i = 0; i < subset.length; i++) { 183 ContentType current = (ContentType) subset[i]; 184 IContentDescriber describer = current.getDescriber(); 185 int status = IContentDescriber.INDETERMINATE; 186 if (describer != null) { 187 if (contents.isText() && !(describer instanceof ITextContentDescriber)) 188 continue; 190 status = current.describe(describer, contents, null); 191 if (status == IContentDescriber.INVALID) 192 continue; 193 } 194 if (status == IContentDescriber.VALID) 195 destination.add(valid++, current); 196 else 197 destination.add(current); 198 } 199 return valid; 200 } 201 202 void dissociate(ContentType contentType, String text, int type) { 203 Map fileSpecMap = ((type & IContentType.FILE_NAME_SPEC) != 0) ? fileNames : fileExtensions; 204 String mappingKey = FileSpec.getMappingKeyFor(text); 205 Set existing = (Set) fileSpecMap.get(mappingKey); 206 if (existing == null) 207 return; 208 existing.remove(contentType); 209 } 210 211 224 private boolean ensureValid(ContentType type) { 225 if (type.getValidation() != ContentType.STATUS_UNKNOWN) 226 return type.isValid(); 228 type.setValidation(ContentType.STATUS_INVALID); 231 if (type.isAlias()) 232 return false; 234 ContentType baseType = null; 236 if (type.getBaseTypeId() != null) { 237 baseType = (ContentType) contentTypes.get(type.getBaseTypeId()); 238 if (baseType == null) 239 return false; 241 baseType = baseType.getAliasTarget(true); 243 ensureValid(baseType); 244 if (baseType.getValidation() != ContentType.STATUS_VALID) 245 return false; 247 } 248 type.setValidation(ContentType.STATUS_VALID); 250 type.setBaseType(baseType); 251 return true; 252 } 253 254 IContentType[] findContentTypesFor(ContentTypeMatcher matcher, InputStream contents, String fileName) throws IOException { 255 final ILazySource buffer = ContentTypeManager.readBuffer(contents); 256 IContentType[] selected = internalFindContentTypesFor(matcher, buffer, fileName, true); 257 ISelectionPolicy policy = matcher.getPolicy(); 259 if (policy != null) 260 selected = applyPolicy(policy, selected, fileName != null, true); 261 return selected; 262 } 263 264 IContentType[] findContentTypesFor(ContentTypeMatcher matcher, final String fileName) { 265 IContentType[] selected = concat(internalFindContentTypesFor(matcher, fileName, policyConstantGeneralIsBetter)); 266 ISelectionPolicy policy = matcher.getPolicy(); 268 if (policy != null) 269 selected = applyPolicy(policy, selected, true, false); 270 return selected; 271 } 272 273 public IContentType[] getAllContentTypes() { 274 List result = new ArrayList(contentTypes.size()); 275 for (Iterator i = contentTypes.values().iterator(); i.hasNext();) { 276 ContentType type = (ContentType) i.next(); 277 if (type.isValid() && !type.isAlias()) 278 result.add(type); 279 } 280 return (IContentType[]) result.toArray(new IContentType[result.size()]); 281 } 282 283 public ContentType[] getChildren(ContentType parent) { 284 ContentType[] children = (ContentType[]) allChildren.get(parent); 285 if (children != null) 286 return children; 287 List result = new ArrayList(5); 288 for (Iterator i = this.contentTypes.values().iterator(); i.hasNext();) { 289 ContentType next = (ContentType) i.next(); 290 if (next.getBaseType() == parent) 291 result.add(next); 292 } 293 children = (ContentType[]) result.toArray(new ContentType[result.size()]); 294 allChildren.put(parent, children); 295 return children; 296 } 297 298 public ContentType getContentType(String contentTypeIdentifier) { 299 ContentType type = internalGetContentType(contentTypeIdentifier); 300 return (type != null && type.isValid() && !type.isAlias()) ? type : null; 301 } 302 303 private IContentDescription getDescriptionFor(ContentTypeMatcher matcher, ILazySource contents, String fileName, QualifiedName[] options) throws IOException { 304 IContentType[] selected = internalFindContentTypesFor(matcher, contents, fileName, false); 305 if (selected.length == 0) 306 return null; 307 ISelectionPolicy policy = matcher.getPolicy(); 309 if (policy != null) { 310 selected = applyPolicy(policy, selected, fileName != null, true); 311 if (selected.length == 0) 312 return null; 313 } 314 return matcher.getSpecificDescription(((ContentType) selected[0]).internalGetDescriptionFor(contents, options)); 315 } 316 317 public IContentDescription getDescriptionFor(ContentTypeMatcher matcher, InputStream contents, String fileName, QualifiedName[] options) throws IOException { 318 return getDescriptionFor(matcher, ContentTypeManager.readBuffer(contents), fileName, options); 319 } 320 321 public IContentDescription getDescriptionFor(ContentTypeMatcher matcher, Reader contents, String fileName, QualifiedName[] options) throws IOException { 322 return getDescriptionFor(matcher, ContentTypeManager.readBuffer(contents), fileName, options); 323 } 324 325 public int getGeneration() { 326 return generation; 327 } 328 329 public ContentTypeManager getManager() { 330 return manager; 331 } 332 333 public boolean internalAccept(ContentTypeVisitor visitor, ContentType root) { 334 if (!root.isValid() || root.isAlias()) 335 return true; 336 int result = visitor.visit(root); 337 switch (result) { 338 case ContentTypeVisitor.STOP : 340 return false; 341 case ContentTypeVisitor.RETURN : 343 return true; 344 } 345 ContentType[] children = getChildren(root); 346 if (children == null) 347 return true; 349 for (int i = 0; i < children.length; i++) 350 if (!internalAccept(visitor, children[i])) 351 return false; 353 return true; 354 } 355 356 public IContentType[] internalFindContentTypesFor(ILazySource buffer, IContentType[][] subset, Comparator validPolicy, Comparator indeterminatePolicy) throws IOException { 357 final List appropriate = new ArrayList(5); 358 final int validFullName = collectMatchingByContents(0, subset[0], appropriate, buffer); 359 final int appropriateFullName = appropriate.size(); 360 final int validExtension = collectMatchingByContents(validFullName, subset[1], appropriate, buffer) - validFullName; 361 final int appropriateExtension = appropriate.size() - appropriateFullName; 362 IContentType[] result = (IContentType[]) appropriate.toArray(new IContentType[appropriate.size()]); 363 if (validFullName > 1) 364 Arrays.sort(result, 0, validFullName, validPolicy); 365 if (validExtension > 1) 366 Arrays.sort(result, validFullName, validFullName + validExtension, validPolicy); 367 if (appropriateFullName - validFullName > 1) 368 Arrays.sort(result, validFullName + validExtension, appropriateFullName + validExtension, indeterminatePolicy); 369 if (appropriateExtension - validExtension > 1) 370 Arrays.sort(result, appropriateFullName + validExtension, appropriate.size(), indeterminatePolicy); 371 return result; 372 } 373 374 private IContentType[] internalFindContentTypesFor(ContentTypeMatcher matcher, ILazySource buffer, String fileName, boolean forceValidation) throws IOException { 375 final IContentType[][] subset; 376 final Comparator validPolicy; 377 Comparator indeterminatePolicy; 378 if (fileName == null) { 379 subset = new IContentType[][] {getAllContentTypes(), NO_CONTENT_TYPES}; 381 indeterminatePolicy = policyConstantGeneralIsBetter; 382 validPolicy = policyConstantSpecificIsBetter; 383 } else { 384 subset = internalFindContentTypesFor(matcher, fileName, policyLexicographical); 385 indeterminatePolicy = policyGeneralIsBetter; 386 validPolicy = policySpecificIsBetter; 387 } 388 int total = subset[0].length + subset[1].length; 389 if (total == 0) 390 return NO_CONTENT_TYPES; 392 if (!forceValidation && total == 1) { 393 IContentType[] found = subset[0].length == 1 ? subset[0] : subset[1]; 395 if (!buffer.isText()) 397 return found; 399 IContentDescriber describer = ((ContentType) found[0]).getDescriber(); 401 if (describer == null || describer instanceof ITextContentDescriber) 402 return found; 404 return NO_CONTENT_TYPES; 406 } 407 return internalFindContentTypesFor(buffer, subset, validPolicy, indeterminatePolicy); 408 } 409 410 416 public IContentType[][] internalFindContentTypesFor(ContentTypeMatcher matcher, final String fileName, Comparator sortingPolicy) { 417 IScopeContext context = matcher.getContext(); 418 IContentType[][] result = {NO_CONTENT_TYPES, NO_CONTENT_TYPES}; 419 420 final Set allByFileName; 421 422 if (context.equals(manager.getContext())) 423 allByFileName = getDirectlyAssociated(fileName, IContentTypeSettings.FILE_NAME_SPEC); 424 else { 425 allByFileName = new HashSet(getDirectlyAssociated(fileName, IContentTypeSettings.FILE_NAME_SPEC | IContentType.IGNORE_USER_DEFINED)); 426 allByFileName.addAll(matcher.getDirectlyAssociated(this, fileName, IContentTypeSettings.FILE_NAME_SPEC)); 427 } 428 Set selectedByName = selectMatchingByName(context, allByFileName, Collections.EMPTY_SET, fileName, IContentType.FILE_NAME_SPEC); 429 result[0] = (IContentType[]) selectedByName.toArray(new IContentType[selectedByName.size()]); 430 final String fileExtension = ContentTypeManager.getFileExtension(fileName); 431 if (fileExtension != null) { 432 final Set allByFileExtension; 433 if (context.equals(manager.getContext())) 434 allByFileExtension = getDirectlyAssociated(fileExtension, IContentTypeSettings.FILE_EXTENSION_SPEC); 435 else { 436 allByFileExtension = new HashSet(getDirectlyAssociated(fileExtension, IContentTypeSettings.FILE_EXTENSION_SPEC | IContentType.IGNORE_USER_DEFINED)); 437 allByFileExtension.addAll(matcher.getDirectlyAssociated(this, fileExtension, IContentTypeSettings.FILE_EXTENSION_SPEC)); 438 } 439 Set selectedByExtension = selectMatchingByName(context, allByFileExtension, selectedByName, fileExtension, IContentType.FILE_EXTENSION_SPEC); 440 if (!selectedByExtension.isEmpty()) 441 result[1] = (IContentType[]) selectedByExtension.toArray(new IContentType[selectedByExtension.size()]); 442 } 443 if (result[0].length > 1) 444 Arrays.sort(result[0], sortingPolicy); 445 if (result[1].length > 1) 446 Arrays.sort(result[1], sortingPolicy); 447 return result; 448 } 449 450 463 public Set getDirectlyAssociated(String text, int typeMask) { 464 Map associations = (typeMask & IContentTypeSettings.FILE_NAME_SPEC) != 0 ? fileNames : fileExtensions; 465 Set result = null; 466 if ((typeMask & (IContentType.IGNORE_PRE_DEFINED | IContentType.IGNORE_USER_DEFINED)) == 0) 467 result = (Set) associations.get(FileSpec.getMappingKeyFor(text)); 469 else { 470 Set initialSet = (Set) associations.get(FileSpec.getMappingKeyFor(text)); 472 if (initialSet != null && !initialSet.isEmpty()) { 473 result = new HashSet(initialSet); 475 typeMask ^= (IContentType.IGNORE_PRE_DEFINED | IContentType.IGNORE_USER_DEFINED); 477 for (Iterator i = result.iterator(); i.hasNext();) { 478 ContentType contentType = (ContentType) i.next(); 479 if (!contentType.hasFileSpec(text, typeMask, true)) 480 i.remove(); 481 } 482 } 483 } 484 return result == null ? Collections.EMPTY_SET : result; 485 } 486 487 ContentType internalGetContentType(String contentTypeIdentifier) { 488 return (ContentType) contentTypes.get(contentTypeIdentifier); 489 } 490 491 void makeAliases() { 492 for (Iterator i = contentTypes.values().iterator(); i.hasNext();) { 494 ContentType type = (ContentType) i.next(); 495 String targetId = type.getAliasTargetId(); 496 if (targetId == null) 497 continue; 498 ContentType target = internalGetContentType(targetId); 499 if (target != null) 500 type.setAliasTarget(target); 501 } 502 } 503 504 507 protected void organize() { 508 makeAliases(); 510 for (Iterator i = contentTypes.values().iterator(); i.hasNext();) { 512 ContentType type = (ContentType) i.next(); 513 if (ensureValid(type)) 514 associate(type); 515 } 516 if (ContentTypeManager.DEBUGGING) 517 for (Iterator i = contentTypes.values().iterator(); i.hasNext();) { 518 ContentType type = (ContentType) i.next(); 519 if (!type.isValid()) 520 ContentMessages.message("Invalid: " + type); } 522 } 523 524 528 private Set selectMatchingByName(final IScopeContext context, Collection source, final Collection existing, final String fileSpecText, final int fileSpecType) { 529 if (source == null || source.isEmpty()) 530 return Collections.EMPTY_SET; 531 final Set destination = new HashSet(5); 532 for (Iterator i = source.iterator(); i.hasNext();) { 534 final ContentType root = (ContentType) i.next(); 535 internalAccept(new ContentTypeVisitor() { 538 public int visit(ContentType type) { 539 if (type != root && type.hasBuiltInAssociations()) 540 return RETURN; 542 if (type == root && !type.hasFileSpec(context, fileSpecText, fileSpecType)) 543 return RETURN; 545 if (!existing.contains(type)) 548 destination.add(type); 549 return CONTINUE; 550 } 551 }, root); 552 } 553 return destination; 554 } 555 } 556 | Popular Tags |