/******************************************************************************* * Copyright (c) 2009 IBM Corporation and others. * 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: * IBM Corporation - initial API and implementation * Zend Technologies *******************************************************************************/ package org.eclipse.dltk.internal.core.index.sql.h2; import java.sql.Connection; import java.sql.SQLException; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.regex.Pattern; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.jobs.ILock; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.dltk.core.IModelElement; import org.eclipse.dltk.core.index.sql.Container; import org.eclipse.dltk.core.index.sql.DbFactory; import org.eclipse.dltk.core.index.sql.Element; import org.eclipse.dltk.core.index.sql.File; import org.eclipse.dltk.core.index.sql.IElementDao; import org.eclipse.dltk.core.index.sql.IElementHandler; import org.eclipse.dltk.core.index.sql.h2.H2Index; import org.eclipse.dltk.core.index2.search.ISearchEngine.MatchRule; /** * This is a cache layer between H2 database and model access * * @author michael */ public class H2Cache { private static final ILock containerLock = Job.getJobManager().newLock(); private static final Map<Integer, Container> containerById = new HashMap<Integer, Container>(); private static final ILock fileLock = Job.getJobManager().newLock(); private static final Map<Integer, Map<Integer, File>> filesByContainer = new HashMap<Integer, Map<Integer, File>>(); private static final ILock elementLock = Job.getJobManager().newLock(); private static final Map<Integer, Map<Integer, List<Element>>> elementsMap = new HashMap<Integer, Map<Integer, List<Element>>>(); private static final ILock loadedLock = Job.getJobManager().newLock(); private static boolean isLoaded; public static void addContainer(Container container) { containerLock.acquire(); try { int containerId = container.getId(); containerById.put(containerId, container); } finally { containerLock.release(); } } public static void addElement(Element element) { elementLock.acquire(); try { int elementType = element.getType(); Map<Integer, List<Element>> elementsByFile = elementsMap .get(elementType); if (elementsByFile == null) { elementsByFile = new HashMap<Integer, List<Element>>(); elementsMap.put(elementType, elementsByFile); } int fileId = element.getFileId(); List<Element> elementsSet = elementsByFile.get(fileId); if (elementsSet == null) { elementsSet = new LinkedList<Element>(); elementsByFile.put(fileId, elementsSet); } elementsSet.add(element); } finally { elementLock.release(); } } public static void addFile(File file) { fileLock.acquire(); try { int containerId = file.getContainerId(); Map<Integer, File> files = filesByContainer.get(containerId); if (files == null) { files = new HashMap<Integer, File>(); filesByContainer.put(containerId, files); } files.put(file.getId(), file); } finally { fileLock.release(); } } public static void deleteContainerById(int id) { containerLock.acquire(); try { containerById.remove(id); deleteFilesByContainerId(id); } finally { containerLock.release(); } } public static void deleteContainerByPath(String path) { containerLock.acquire(); try { Container container = selectContainerByPath(path); if (container != null) { deleteContainerById(container.getId()); } } finally { containerLock.release(); } } public static void deleteElementsByFileId(int id) { elementLock.acquire(); try { Iterator<Map<Integer, List<Element>>> i = elementsMap.values() .iterator(); while (i.hasNext()) { Map<Integer, List<Element>> elementsByFile = i.next(); elementsByFile.remove(id); } } finally { elementLock.release(); } } public static void deleteFileByContainerIdAndPath(int containerId, String path) { fileLock.acquire(); try { File file = selectFileByContainerIdAndPath(containerId, path); if (file != null) { deleteFileById(file.getId()); } } finally { fileLock.release(); } } public static void deleteFileById(int id) { fileLock.acquire(); try { Iterator<Map<Integer, File>> i = filesByContainer.values() .iterator(); while (i.hasNext()) { i.next().remove(id); } deleteElementsByFileId(id); } finally { fileLock.release(); } } public static void deleteFilesByContainerId(int id) { fileLock.acquire(); try { Map<Integer, File> files = filesByContainer.remove(id); if (files != null) { Iterator<Integer> i = files.keySet().iterator(); while (i.hasNext()) { deleteElementsByFileId(i.next()); } } } finally { fileLock.release(); } } public static Container selectContainerById(int id) { containerLock.acquire(); try { return containerById.get(id); } finally { containerLock.release(); } } public static Container selectContainerByPath(String path) { containerLock.acquire(); try { Iterator<Container> i = containerById.values().iterator(); while (i.hasNext()) { Container container = i.next(); if (container.getPath().equals(path)) { return container; } } return null; } finally { containerLock.release(); } } public static Collection<Element> selectElementsByFileId(int id) { elementLock.acquire(); try { List<Element> elements = new LinkedList<Element>(); Iterator<Map<Integer, List<Element>>> i = elementsMap.values() .iterator(); while (i.hasNext()) { Map<Integer, List<Element>> elementsByFile = i.next(); List<Element> l = elementsByFile.get(id); if (l != null) { elements.addAll(l); } } return elements; } finally { elementLock.release(); } } public static File selectFileByContainerIdAndPath(int containerId, String path) { fileLock.acquire(); try { Map<Integer, File> files = filesByContainer.get(containerId); if (files != null) { Iterator<File> i = files.values().iterator(); while (i.hasNext()) { File file = i.next(); if (file.getPath().equals(path)) { return file; } } } } finally { fileLock.release(); } return null; } public static File selectFileById(int id) { fileLock.acquire(); try { Iterator<Map<Integer, File>> i = filesByContainer.values() .iterator(); while (i.hasNext()) { File file = i.next().get(id); if (file != null) { return file; } } return null; } finally { fileLock.release(); } } public static Collection<File> selectFilesByContainerId(int id) { fileLock.acquire(); try { Map<Integer, File> files = filesByContainer.get(id); if (files != null) { return files.values(); } return Collections.emptyList(); } finally { fileLock.release(); } } public static Collection<Element> searchElements(String pattern, MatchRule matchRule, int elementType, int trueFlags, int falseFlags, String qualifier, String parent, int[] filesId, int containersId[], String natureId, int limit) { Set<Integer> filesIds = new HashSet<Integer>(); if (filesId != null) { for (int fileId : filesId) { filesIds.add(fileId); } } else if (containersId != null) { containerLock.acquire(); try { for (int containerId : containersId) { fileLock.acquire(); try { Map<Integer, File> files = filesByContainer .get(containerId); if (files != null) { filesIds.addAll(files.keySet()); } } finally { fileLock.release(); } } } finally { containerLock.release(); } } elementLock.acquire(); try { Set<String> patternSet = null; Pattern posixPattern = null; // Pre-cache pattern's lower and upper case variants: String patternLC = null; String patternUC = null; if (pattern != null) { patternLC = pattern.toLowerCase(); patternUC = pattern.toUpperCase(); } if (matchRule == MatchRule.SET) { patternSet = new HashSet<String>(); String[] parts = pattern.split(","); for (String part : parts) { if (part.length() > 0) { patternSet.add(part.toLowerCase()); } } } else if (matchRule == MatchRule.PATTERN) { posixPattern = createPosixPattern(pattern); } List<Element> result = new LinkedList<Element>(); Map<Integer, List<Element>> elementsByFile = elementsMap .get(elementType); if (elementsByFile != null) { if (filesIds.size() == 0) { Iterator<List<Element>> i = elementsByFile.values() .iterator(); while (i.hasNext()) { searchInElements(i.next(), result, pattern, matchRule, trueFlags, falseFlags, qualifier, parent, patternSet, posixPattern, patternLC, patternUC, limit); } } else { for (Integer fileId : filesIds) { searchInElements(elementsByFile.get(fileId), result, pattern, matchRule, trueFlags, falseFlags, qualifier, parent, patternSet, posixPattern, patternLC, patternUC, limit); } } } return result; } finally { elementLock.release(); } } private static void searchInElements(List<Element> elements, List<Element> result, String pattern, MatchRule matchRule, int trueFlags, int falseFlags, String qualifier, String parent, Set<String> patternSet, Pattern posixPattern, String patternLC, String patternUC, int limit) { if (elements != null) { Iterator<Element> i = elements.iterator(); while (i.hasNext()) { Element element = i.next(); if (elementMatches(element, pattern, matchRule, trueFlags, falseFlags, qualifier, parent, patternSet, posixPattern, patternLC, patternUC)) { result.add(element); if (--limit == 0) { break; } } } } } private static boolean elementMatches(Element element, String pattern, MatchRule matchRule, int trueFlags, int falseFlags, String qualifier, String parent, Set<String> patternSet, Pattern posixPattern, String patternLC, String patternUC) { if ((trueFlags == 0 || (element.getFlags() & trueFlags) != 0) && (falseFlags == 0 || (element.getFlags() & falseFlags) == 0)) { if (qualifier == null || qualifier.length() == 0 || qualifier.equals(element.getQualifier())) { if (parent == null || parent.length() == 0 || parent.equals(element.getParent())) { String elementName = element.getName(); if (pattern == null || pattern.length() == 0 || (matchRule == MatchRule.EXACT && pattern .equalsIgnoreCase(elementName)) || (matchRule == MatchRule.PREFIX && startsWithIgnoreCase( elementName, patternLC)) || (matchRule == MatchRule.CAMEL_CASE && element.getCamelCaseName() != null && element .getCamelCaseName().startsWith(patternUC)) || (matchRule == MatchRule.SET && patternSet .contains(elementName.toLowerCase())) || (matchRule == MatchRule.PATTERN && posixPattern .matcher(elementName).matches())) { return true; } } } } return false; } private static Pattern createPosixPattern(String pattern) { StringBuilder buf = new StringBuilder(); boolean inQuoted = false; for (int i = 0; i < pattern.length(); ++i) { char ch = pattern.charAt(i); if (ch == '*') { if (inQuoted) { buf.append("\\E"); inQuoted = false; } buf.append(".*"); } else if (ch == '?') { if (inQuoted) { buf.append("\\E"); inQuoted = false; } buf.append(".?"); } else { if (!inQuoted) { buf.append("\\Q"); inQuoted = true; } buf.append(ch); } } return Pattern.compile(buf.toString(), Pattern.CASE_INSENSITIVE); } private static boolean startsWithIgnoreCase(String str, String prefix) { return startsWith(str, prefix, true); } private static boolean startsWith(String str, String prefix, boolean ignoreCase) { if (str == null || prefix == null) { return (str == null && prefix == null); } if (prefix.length() > str.length()) { return false; } return str.regionMatches(ignoreCase, 0, prefix, 0, prefix.length()); } public static boolean isLoaded() { loadedLock.acquire(); try { return isLoaded; } finally { loadedLock.release(); } } public static void load() { loadedLock.acquire(); try { if (!isLoaded) { try { DbFactory dbFactory = DbFactory.getInstance(); Connection connection = dbFactory.createConnection(); try { IElementDao elementDao = dbFactory.getElementDao(); elementDao.search(connection, null, MatchRule.PREFIX, IModelElement.FIELD, 0, 0, null, null, null, null, "org.eclipse.php.core.PHPNature", 0, false, new IElementHandler() { public void handle(Element element) { } }, new NullProgressMonitor()); elementDao.search(connection, null, MatchRule.PREFIX, IModelElement.TYPE, 0, 0, null, null, null, null, "org.eclipse.php.core.PHPNature", 0, false, new IElementHandler() { public void handle(Element element) { } }, new NullProgressMonitor()); elementDao.search(connection, null, MatchRule.PREFIX, IModelElement.METHOD, 0, 0, null, null, null, null, "org.eclipse.php.core.PHPNature", 0, false, new IElementHandler() { public void handle(Element element) { } }, new NullProgressMonitor()); elementDao.search(connection, null, MatchRule.PREFIX, IModelElement.IMPORT_DECLARATION, 0, 0, null, null, null, null, "org.eclipse.php.core.PHPNature", 0, false, new IElementHandler() { public void handle(Element element) { } }, new NullProgressMonitor()); } finally { connection.close(); } } catch (SQLException e) { if (H2Index.DEBUG) { e.printStackTrace(); } } finally { isLoaded = true; } } } finally { loadedLock.release(); } } }