| /******************************************************************************* |
| * Copyright (c) 2008, 2016 xored software, Inc. |
| * |
| * This program and the accompanying materials are made available under the |
| * terms of the Eclipse Public License v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0. |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * 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; |
| } |
| |
| } |