/*
* Copyright 2009-2017 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eclipse.jdt.groovy.core.util;
import static org.eclipse.jdt.internal.core.util.Util.getJavaLikeExtensions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Set;
import java.util.TreeSet;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.content.IContentType;
import org.eclipse.core.runtime.content.IContentTypeManager;
import org.eclipse.core.runtime.content.IContentTypeManager.ContentTypeChangeEvent;
import org.eclipse.jdt.internal.core.util.Util;
/**
* Utility methods for dealing with Groovy content types.
*/
public class ContentTypeUtils {
private ContentTypeUtils() {}
/**
* Returns the registered Gradle-like extensions.
*/
public static char[][] getGradleLikeExtensions() {
if (GRADLE_LIKE_EXTENSIONS == null) {
IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
if (contentTypeManager != null) {
IContentType gradleContentType = contentTypeManager.getContentType(GRADLE_SCRIPT_CONTENT_TYPE);
String[] gradleFileExtensions = gradleContentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC);
GRADLE_LIKE_EXTENSIONS = toCharArrayArray(Arrays.asList(gradleFileExtensions));
} else {
GRADLE_LIKE_EXTENSIONS = new char[][] {"gradle".toCharArray()};
}
}
return GRADLE_LIKE_EXTENSIONS;
}
/**
* Returns the registered Groovy-like extensions.
*/
public static char[][] getGroovyLikeExtensions() {
if (GROOVY_LIKE_EXTENSIONS == null) {
IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
if (contentTypeManager != null) {
Set<String> extensions = new TreeSet<String>(new Comparator<String>() {
public int compare(String s1, String s2) {
if (s1.equals("groovy")) return -1;
if (s2.equals("groovy")) return +1;
return s1.compareTo(s2);
}
});
IContentType groovyContentType = contentTypeManager.getContentType(GROOVY_SOURCE_CONTENT_TYPE);
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=121715
// content types derived from Groovy content type should be included
for (IContentType contentType : contentTypeManager.getAllContentTypes()) {
if (contentType.isKindOf(groovyContentType)) {
for (String fileExtension : contentType.getFileSpecs(IContentType.FILE_EXTENSION_SPEC)) {
extensions.add(fileExtension);
}
}
}
if (extensions.isEmpty()) {
// Shouldn't happen, but it seems it does. See: STS-3936
// Probably means user's workspace is already severely broken...
// Still it is not nice to throw exceptions. So handle the case and log an error instead.
if (!noGroovyContentTypesErrorLogged) {
noGroovyContentTypesErrorLogged = true;
Util.log(new IllegalStateException("No Groovy Content Types found. This shouldn't happen. Is the workspace metadata corrupted?"));
}
// Don't cache it. Maybe its only looking funky because we are looking 'too early'.
return new char[][] {"groovy".toCharArray()};
}
GROOVY_LIKE_EXTENSIONS = toCharArrayArray(extensions);
} else {
GROOVY_LIKE_EXTENSIONS = new char[][] {"groovy".toCharArray()};
}
}
return GROOVY_LIKE_EXTENSIONS;
}
/**
* Returns the registered Java-like extensions (excluding the Groovy-like ones).
*/
public static char[][] getJavaButNotGroovyLikeExtensions() {
if (JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS == null) {
char[][] javaLikeExtensions = getJavaLikeExtensions();
char[][] groovyLikeExtensiosn = getGroovyLikeExtensions();
List<char[]> interestingExtensions = new ArrayList<char[]>();
for (char[] javaLike : javaLikeExtensions) {
boolean found = false;
for (char[] groovyLike : groovyLikeExtensiosn) {
if (Arrays.equals(javaLike, groovyLike)) {
found = true;
break;
}
}
if (!found) {
interestingExtensions.add(javaLike);
}
}
JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS = interestingExtensions.toArray(new char[interestingExtensions.size()][]);
// ensure "java" is first
int javaIndex = 0;
char[] javaArr = "java".toCharArray();
while (javaIndex < JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS.length) {
if (Arrays.equals(javaArr, JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS[javaIndex])) {
break;
}
javaIndex++;
}
if (javaIndex < JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS.length) {
JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS[javaIndex] = JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS[0];
JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS[0] = javaArr;
} else {
Util.log(null, "'java' not registered as a java-like extension");
}
}
return JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS;
}
/**
* Determines if the given file is a Gradle script file.
*
* @param fileName absolute path or simple name is fine
* @return {@code true} iff the file name is Gradle-like
*/
public static boolean isGradleLikeFileName(char[] fileName) {
if (fileName != null && fileName.length > 0) {
return isGradleLikeFileName(new CharArraySequence(fileName));
}
return false;
}
/**
* Determines if the given file is a Gradle script file.
*
* @param fileName absolute path or simple name is fine
* @return {@code true} iff the file name is Gradle-like
*/
public static boolean isGradleLikeFileName(CharSequence fileName) {
if (fileName != null && fileName.length() > 0) {
if (endsWithAny(fileName, getGradleLikeExtensions())) {
return true;
}
// TODO: Gradle file names
}
return false;
}
/**
* Determines if the given file is a Groovy source file.
*
* @param fileName absolute path or simple name is fine
* @return {@code true} iff the file name is Groovy-like
*/
public static boolean isGroovyLikeFileName(char[] fileName) {
if (fileName != null && fileName.length > 0) {
return isGroovyLikeFileName(new CharArraySequence(fileName));
}
return false;
}
/**
* Determines if the given file is a Groovy source file.
*
* @param fileName absolute path or simple name is fine
* @return {@code true} iff the file name is Groovy-like
*/
public static boolean isGroovyLikeFileName(CharSequence fileName) {
if (fileName != null && fileName.length() > 0) {
if (endsWithAny(fileName, getGroovyLikeExtensions())) {
return true;
}
if (GROOVY_FILE_NAMES == null) {
GROOVY_FILE_NAMES = loadGroovyFileNames();
}
fileName = fileName.subSequence(
lastIndexOf(fileName, '/') + 1, fileName.length());
return GROOVY_FILE_NAMES.contains(fileName.toString());
}
return false;
}
/**
* Determines if the given file is a Java source file.
*
* @param fileName absolute path or simple name is fine
* @return {@code true} iff the file name is Java-like (but not Groovy-like)
*/
public static boolean isJavaLikeButNotGroovyLikeFileName(CharSequence fileName) {
if (fileName != null && fileName.length() > 0) {
if (endsWithAny(fileName, getJavaButNotGroovyLikeExtensions())) {
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
static {
IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
if (contentTypeManager != null) {
contentTypeManager.addContentTypeChangeListener(new IContentTypeManager.IContentTypeChangeListener() {
public void contentTypeChanged(ContentTypeChangeEvent event) {
// we can be more specific here, but content types change so rarely, that
// I am not concerned about being overly eager to invalidate the cache
GROOVY_FILE_NAMES = null;
GROOVY_LIKE_EXTENSIONS = null;
GRADLE_LIKE_EXTENSIONS = null;
JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS = null;
}
});
}
}
private static Set<String> GROOVY_FILE_NAMES;
private static char[][] GROOVY_LIKE_EXTENSIONS;
private static char[][] GRADLE_LIKE_EXTENSIONS;
private static char[][] JAVA_LIKE_BUT_NOT_GROOVY_LIKE_EXTENSIONS;
private static final String GRADLE_SCRIPT_CONTENT_TYPE = "org.eclipse.jdt.groovy.core.gradleScript";
private static final String GROOVY_SOURCE_CONTENT_TYPE = "org.eclipse.jdt.groovy.core.groovySource";
private static boolean noGroovyContentTypesErrorLogged = false; // To avoid spamming error into the log repeatedly.
private static Set<String> loadGroovyFileNames() {
IContentTypeManager contentTypeManager = Platform.getContentTypeManager();
if (contentTypeManager != null) {
Set<String> names = null;
IContentType groovyContentType = contentTypeManager.getContentType(GROOVY_SOURCE_CONTENT_TYPE);
for (IContentType contentType : contentTypeManager.getAllContentTypes()) {
if (contentType.isKindOf(groovyContentType)) {
for (String fileName : contentType.getFileSpecs(IContentType.FILE_NAME_SPEC)) {
if (names == null) names = new TreeSet<String>();
names.add(fileName);
}
}
}
if (names != null) return names;
}
return Collections.emptySet();
}
//--------------------------------------------------------------------------
private static boolean endsWithAny(CharSequence sequence, char[]... extensions) {
int length = sequence.length();
for (char[] extension : extensions) {
int offset = (length - extension.length);
if (offset < 1 || sequence.charAt(offset - 1) != '.') {
continue;
}
if (sequence.subSequence(offset, length).toString().equals(String.valueOf(extension))) {
return true;
}
}
return false;
}
private static int lastIndexOf(CharSequence sequence, char character) {
for (int i = sequence.length() - 1; i >= 0; i -= 1) {
if (sequence.charAt(i) == character) {
return i;
}
}
return -1;
}
private static char[][] toCharArrayArray(Collection<String> strings) {
int i = 0, n = strings.size();
char[][] arrays = new char[n][];
for (String string : strings) {
arrays[i++] = string.toCharArray();
}
return arrays;
}
}