blob: db8d583c9a888dbfe15dc54a3c73f41676b98e0a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2016 xored software, Inc.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* xored software, Inc. - initial API and Implementation (Alex Panchenko)
*******************************************************************************/
package org.eclipse.dltk.internal.core;
import java.util.Set;
import org.eclipse.core.runtime.preferences.DefaultScope;
import org.eclipse.core.runtime.preferences.IEclipsePreferences;
import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.dltk.compiler.CharOperation;
import org.eclipse.dltk.core.DLTKCore;
import org.eclipse.dltk.core.DLTKLanguageManager;
import org.eclipse.dltk.core.IDLTKAssociationManager;
public class DLTKAssociationManager implements IDLTKAssociationManager {
private final String natureId;
private final String qualifier;
public DLTKAssociationManager(String natureId, String qualifier) {
this.natureId = natureId;
this.qualifier = qualifier;
}
private char[][] cachedPatterns = null;
@Override
public boolean isAssociatedWith(String name) {
char[][] patterns;
synchronized (this) {
if (cachedPatterns == null) {
final IEclipsePreferences prefs = getEclipsePreferences();
initPatterns(prefs.get(
DLTKCore.LANGUAGE_FILENAME_ASSOCIATIONS,
DefaultScope.INSTANCE.getNode(qualifier).get(
DLTKCore.LANGUAGE_FILENAME_ASSOCIATIONS, null)));
}
patterns = cachedPatterns;
}
if (patterns.length != 0) {
for (int i = 0; i < patterns.length; ++i) {
final char[] pattern = patterns[i];
if (match(pattern, name)) {
return true;
}
}
}
return false;
}
/**
* @param value
*/
private void initPatterns(String value) {
final Set<String> patterns = DLTKLanguageManager
.loadFilenameAssociations(this.natureId);
if (value != null && value.length() != 0 || !patterns.isEmpty()) {
char[][] patterns1 = value != null && value.length() != 0 ? CharOperation
.splitOn(DLTKCore.LANGUAGE_FILENAME_ASSOCIATION_SEPARATOR,
value.toCharArray())
: null;
char[][] patterns2 = !patterns.isEmpty() ? CharOperation
.stringArrayToCharCharArray(patterns
.toArray(new String[patterns.size()])) : null;
cachedPatterns = CharOperation.arrayConcat(patterns1, patterns2);
} else {
cachedPatterns = CharOperation.NO_CHAR_CHAR;
}
}
private static final boolean match(char[] pattern, String name) {
final int patternEnd = pattern.length;
final int nameEnd = name.length();
int iPattern = 0; // patternStart;
int iName = 0; // nameStart
/* check first segment */
char patternChar = 0;
while ((iPattern < patternEnd)
&& (patternChar = pattern[iPattern]) != '*') {
if (iName == nameEnd)
return false;
if (patternChar != name.charAt(iName) && patternChar != '?') {
return false;
}
iName++;
iPattern++;
}
/* check sequence of star+segment */
int segmentStart;
if (patternChar == '*') {
segmentStart = ++iPattern; // skip star
} else {
segmentStart = 0; // force iName check
}
int prefixStart = iName;
checkSegment: while (iName < nameEnd) {
if (iPattern == patternEnd) {
iPattern = segmentStart; // mismatch - restart current
// segment
iName = ++prefixStart;
continue checkSegment;
}
/* segment is ending */
if ((patternChar = pattern[iPattern]) == '*') {
segmentStart = ++iPattern; // skip start
if (segmentStart == patternEnd) {
return true;
}
prefixStart = iName;
continue checkSegment;
}
/* check current name character */
if (Character.toLowerCase(name.charAt(iName)) != patternChar
&& patternChar != '?') {
iPattern = segmentStart; // mismatch - restart current
// segment
iName = ++prefixStart;
continue checkSegment;
}
iName++;
iPattern++;
}
return (segmentStart == patternEnd)
|| (iName == nameEnd && iPattern == patternEnd)
|| (iPattern == patternEnd - 1 && pattern[iPattern] == '*');
}
private final Object preferencesLock = new Object();
private IEclipsePreferences preferences = null;
private IEclipsePreferences getEclipsePreferences() {
synchronized (preferencesLock) {
if (preferences != null) {
return preferences;
}
}
final IEclipsePreferences eclipsePreferences = InstanceScope.INSTANCE
.getNode(qualifier);
synchronized (preferencesLock) {
this.preferences = eclipsePreferences;
}
// Listen to node removal from parent in order to reset cache (see bug
// 68993)
IEclipsePreferences.INodeChangeListener nodeListener = new IEclipsePreferences.INodeChangeListener() {
@Override
public void added(IEclipsePreferences.NodeChangeEvent event) {
// do nothing
}
@Override
public void removed(IEclipsePreferences.NodeChangeEvent event) {
if (event.getChild() == eclipsePreferences) {
synchronized (preferencesLock) {
DLTKAssociationManager.this.preferences = null;
}
}
}
};
((IEclipsePreferences) eclipsePreferences.parent())
.addNodeChangeListener(nodeListener);
// Listen to preference changes
IEclipsePreferences.IPreferenceChangeListener preferenceListener = new IEclipsePreferences.IPreferenceChangeListener() {
@Override
public void preferenceChange(
IEclipsePreferences.PreferenceChangeEvent event) {
synchronized (this) {
initPatterns((String) event.getNewValue());
}
}
};
eclipsePreferences.addPreferenceChangeListener(preferenceListener);
return eclipsePreferences;
}
}