1 /*******************************************************************************
2  * Copyright (c) 2005, 2017 IBM Corporation and others.
3  *
4  * This program and the accompanying materials
5  * are made available under the terms of the Eclipse Public License 2.0
6  * which accompanies this distribution, and is available at
7  * https://www.eclipse.org/legal/epl-2.0/
8  *
9  * SPDX-License-Identifier: EPL-2.0
10  *
11  * Contributors:
12  *     IBM Corporation - initial API and implementation
13  *     Mickael Istria (Red Hat Inc.) - [263316] regexp for file association
14  *******************************************************************************/
15 package org.eclipse.core.internal.content;
16 
17 import java.io.*;
18 import java.util.*;
19 import java.util.regex.Pattern;
20 import org.eclipse.core.runtime.QualifiedName;
21 import org.eclipse.core.runtime.content.*;
22 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
23 import org.eclipse.core.runtime.preferences.IScopeContext;
24 import org.osgi.service.prefs.BackingStoreException;
25 
26 /**
27  * @since 3.1
28  */
29 public class ContentTypeMatcher implements IContentTypeMatcher {
30 
31 	private IScopeContext context;
32 	private IContentTypeManager.ISelectionPolicy policy;
33 
ContentTypeMatcher(IContentTypeManager.ISelectionPolicy policy, IScopeContext context)34 	public ContentTypeMatcher(IContentTypeManager.ISelectionPolicy policy, IScopeContext context) {
35 		this.policy = policy;
36 		this.context = context;
37 	}
38 
39 	@Override
findContentTypeFor(InputStream contents, String fileName)40 	public IContentType findContentTypeFor(InputStream contents, String fileName) throws IOException {
41 		ContentTypeCatalog currentCatalog = getCatalog();
42 		IContentType[] all = currentCatalog.findContentTypesFor(this, contents, fileName);
43 		return all.length > 0 ? new ContentTypeHandler((ContentType) all[0], currentCatalog.getGeneration()) : null;
44 	}
45 
46 	@Override
findContentTypeFor(String fileName)47 	public IContentType findContentTypeFor(String fileName) {
48 		// basic implementation just gets all content types
49 		ContentTypeCatalog currentCatalog = getCatalog();
50 		IContentType[] associated = currentCatalog.findContentTypesFor(this, fileName);
51 		return associated.length == 0 ? null : new ContentTypeHandler((ContentType) associated[0], currentCatalog.getGeneration());
52 	}
53 
54 	@Override
findContentTypesFor(InputStream contents, String fileName)55 	public IContentType[] findContentTypesFor(InputStream contents, String fileName) throws IOException {
56 		ContentTypeCatalog currentCatalog = getCatalog();
57 		IContentType[] types = currentCatalog.findContentTypesFor(this, contents, fileName);
58 		IContentType[] result = new IContentType[types.length];
59 		int generation = currentCatalog.getGeneration();
60 		for (int i = 0; i < result.length; i++)
61 			result[i] = new ContentTypeHandler((ContentType) types[i], generation);
62 		return result;
63 	}
64 
65 	@Override
findContentTypesFor(String fileName)66 	public IContentType[] findContentTypesFor(String fileName) {
67 		ContentTypeCatalog currentCatalog = getCatalog();
68 		IContentType[] types = currentCatalog.findContentTypesFor(this, fileName);
69 		IContentType[] result = new IContentType[types.length];
70 		int generation = currentCatalog.getGeneration();
71 		for (int i = 0; i < result.length; i++)
72 			result[i] = new ContentTypeHandler((ContentType) types[i], generation);
73 		return result;
74 	}
75 
getCatalog()76 	private ContentTypeCatalog getCatalog() {
77 		return ContentTypeManager.getInstance().getCatalog();
78 	}
79 
80 	@Override
getDescriptionFor(InputStream contents, String fileName, QualifiedName[] options)81 	public IContentDescription getDescriptionFor(InputStream contents, String fileName, QualifiedName[] options) throws IOException {
82 		return getCatalog().getDescriptionFor(this, contents, fileName, options);
83 	}
84 
85 	@Override
getDescriptionFor(Reader contents, String fileName, QualifiedName[] options)86 	public IContentDescription getDescriptionFor(Reader contents, String fileName, QualifiedName[] options) throws IOException {
87 		return getCatalog().getDescriptionFor(this, contents, fileName, options);
88 	}
89 
getContext()90 	public IScopeContext getContext() {
91 		return context;
92 	}
93 
getPolicy()94 	public IContentTypeManager.ISelectionPolicy getPolicy() {
95 		return policy;
96 	}
97 
98 	/**
99 	 * Enumerates all content types whose settings satisfy the given file spec type mask.
100 	 */
getDirectlyAssociated(final ContentTypeCatalog catalog, final String fileSpec, final int typeMask)101 	public Collection<ContentType> getDirectlyAssociated(final ContentTypeCatalog catalog, final String fileSpec, final int typeMask) {
102 		if ((typeMask & (IContentType.FILE_EXTENSION_SPEC | IContentType.FILE_NAME_SPEC)) == 0) {
103 			throw new IllegalArgumentException("This method only apply to name or extension based associations"); //$NON-NLS-1$
104 		}
105 		//TODO: make sure we include built-in associations as well
106 		final IEclipsePreferences root = context.getNode(ContentTypeManager.CONTENT_TYPE_PREF_NODE);
107 		final Set<ContentType> result = new HashSet<>(3);
108 		try {
109 			root.accept(node -> {
110 				if (node == root)
111 					return true;
112 				String[] fileSpecs = ContentTypeSettings.getFileSpecs(node, typeMask);
113 				for (String fileSpecification : fileSpecs)
114 					if (fileSpecification.equalsIgnoreCase(fileSpec)) {
115 						ContentType associated = catalog.getContentType(node.name());
116 						if (associated != null)
117 							result.add(associated);
118 						break;
119 					}
120 				return false;
121 			});
122 		} catch (BackingStoreException bse) {
123 			ContentType.log(ContentMessages.content_errorLoadingSettings, bse);
124 		}
125 		return result == null ? Collections.EMPTY_SET : result;
126 	}
127 
getMatchingRegexpAssociated(ContentTypeCatalog catalog, String fileName, final int typeMask)128 	public Collection<? extends ContentType> getMatchingRegexpAssociated(ContentTypeCatalog catalog,
129 			String fileName, final int typeMask) {
130 		if ((typeMask & IContentType.FILE_PATTERN_SPEC) == 0) {
131 			throw new IllegalArgumentException("This method only applies for FILE_REGEXP_SPEC."); //$NON-NLS-1$
132 		}
133 		final IEclipsePreferences root = context.getNode(ContentTypeManager.CONTENT_TYPE_PREF_NODE);
134 		final Set<ContentType> result = new HashSet<>(3);
135 		try {
136 			root.accept(node -> {
137 				if (node == root)
138 					return true;
139 				String[] fileSpecs = ContentTypeSettings.getFileSpecs(node, typeMask);
140 				for (String fileSpecification : fileSpecs)
141 					if (Pattern.matches(catalog.toRegexp(fileSpecification), fileName)) {
142 						ContentType associated = catalog.getContentType(node.name());
143 						if (associated != null)
144 							result.add(associated);
145 						break;
146 					}
147 				return false;
148 			});
149 		} catch (BackingStoreException bse) {
150 			ContentType.log(ContentMessages.content_errorLoadingSettings, bse);
151 		}
152 		return result == null ? Collections.EMPTY_SET : result;
153 	}
154 
getSpecificDescription(BasicDescription description)155 	public IContentDescription getSpecificDescription(BasicDescription description) {
156 		if (description == null || ContentTypeManager.getInstance().getContext().equals(getContext()))
157 			// no need for specific content descriptions
158 			return description;
159 		// default description
160 		if (description instanceof DefaultDescription)
161 			// return an context specific description instead
162 			return new DefaultDescription(new ContentTypeSettings((ContentType) description.getContentTypeInfo(), context));
163 		// non-default description
164 		// replace info object with context specific settings
165 		((ContentDescription) description).setContentTypeInfo(new ContentTypeSettings((ContentType) description.getContentTypeInfo(), context));
166 		return description;
167 	}
168 
169 }
170