| /******************************************************************************* |
| * Copyright (c) 2008, 2017 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.core.search.indexing.core; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.dltk.compiler.CharOperation; |
| import org.eclipse.dltk.core.DLTKCore; |
| import org.eclipse.dltk.core.IDLTKLanguageToolkit; |
| import org.eclipse.dltk.core.IExternalSourceModule; |
| import org.eclipse.dltk.core.IModelElement; |
| import org.eclipse.dltk.core.IModelElementVisitor; |
| import org.eclipse.dltk.core.IProjectFragment; |
| import org.eclipse.dltk.core.ISourceModule; |
| import org.eclipse.dltk.core.ModelException; |
| import org.eclipse.dltk.core.environment.EnvironmentManager; |
| import org.eclipse.dltk.core.environment.EnvironmentPathUtils; |
| import org.eclipse.dltk.core.environment.IEnvironment; |
| import org.eclipse.dltk.core.environment.IFileHandle; |
| import org.eclipse.dltk.core.search.index.Index; |
| import org.eclipse.dltk.core.search.indexing.IIndexConstants; |
| import org.eclipse.dltk.core.search.indexing.IProjectIndexer; |
| import org.eclipse.dltk.core.search.indexing.ReadWriteMonitor; |
| import org.eclipse.dltk.internal.core.BuiltinSourceModule; |
| |
| public class ArchiveProjectFragmentRequest extends IndexRequest { |
| |
| protected final IProjectFragment fragment; |
| protected final IDLTKLanguageToolkit toolkit; |
| |
| public ArchiveProjectFragmentRequest(IProjectIndexer indexer, |
| IProjectFragment fragment, IDLTKLanguageToolkit toolkit) { |
| super(indexer); |
| this.fragment = fragment; |
| this.toolkit = toolkit; |
| } |
| |
| @Override |
| protected String getName() { |
| return fragment.getElementName(); |
| } |
| |
| @Override |
| protected void run() throws CoreException, IOException { |
| IEnvironment environment = EnvironmentManager |
| .getEnvironment(fragment.getScriptProject()); |
| if (environment == null || !environment.connect()) { |
| return; |
| } |
| final Set<ISourceModule> modules = getExternalSourceModules(); |
| final Index index = getIndexer().getProjectFragmentIndex(fragment); |
| if (index == null) { |
| return; |
| } |
| final IFileHandle archive = EnvironmentPathUtils |
| .getFile(fragment.getPath()); |
| if (archive == null) { |
| return; |
| } |
| final String signature = archive.lastModified() + "#" |
| + archive.length(); |
| final IPath containerPath = fragment.getPath(); |
| final List<Object> changes = checkChanges(index, modules, containerPath, |
| signature); |
| if (DEBUG) { |
| log("changes.size=" + changes.size()); //$NON-NLS-1$ |
| } |
| if (changes.isEmpty()) { |
| return; |
| } |
| final ReadWriteMonitor imon = index.monitor; |
| imon.enterWrite(); |
| try { |
| index.separator = Index.JAR_SEPARATOR; |
| index.addIndexEntry(IIndexConstants.STAMP, CharOperation.NO_CHAR, |
| SIGNATURE_PREFIX + signature); |
| for (Iterator<Object> i = changes.iterator(); !isCancelled |
| && i.hasNext();) { |
| final Object change = i.next(); |
| if (change instanceof String) { |
| index.remove((String) change); |
| } else if (change instanceof ISourceModule) { |
| ISourceModule module = (ISourceModule) change; |
| getIndexer().indexSourceModule(index, toolkit, module, |
| containerPath); |
| } |
| } |
| |
| } catch (Throwable e) { |
| e.printStackTrace(); |
| } finally { |
| try { |
| index.save(); |
| } catch (IOException e) { |
| DLTKCore.error("error saving index", e); //$NON-NLS-1$ |
| } finally { |
| imon.exitWrite(); |
| } |
| } |
| } |
| |
| // TODO (alex) use content cache for it probably |
| private static final String SIGNATURE_PREFIX = "###.LIBRARY.###.SIGNATURE.###"; |
| |
| protected List<Object> checkChanges(Index index, |
| Collection<ISourceModule> modules, IPath containerPath, |
| String signature) throws ModelException, IOException { |
| final String[] documents = queryDocumentNames(index); |
| if (documents != null && documents.length != 0) { |
| final List<Object> changes = new ArrayList<>(); |
| final Map<String, ISourceModule> m = collectSourceModulePaths( |
| modules, containerPath); |
| if (DEBUG) { |
| log("documents.length=" + documents.length); //$NON-NLS-1$ |
| log("modules.size=" + modules.size()); //$NON-NLS-1$ |
| log("map.size=" + m.size()); //$NON-NLS-1$ |
| } |
| boolean signatureOK = false; |
| final List<ISourceModule> updates = new ArrayList<>(); |
| for (int i = 0; i < documents.length; ++i) { |
| final String document = documents[i]; |
| if (document.startsWith(SIGNATURE_PREFIX)) { |
| signatureOK = document.substring(SIGNATURE_PREFIX.length()) |
| .equals(signature); |
| if (!signatureOK) { |
| changes.add(document); |
| } |
| } else { |
| final ISourceModule module = m.remove(document); |
| if (module == null) { |
| changes.add(document); |
| } else { |
| updates.add(module); |
| } |
| } |
| } |
| if (!signatureOK) { |
| changes.addAll(updates); |
| } |
| if (!m.isEmpty()) { |
| changes.addAll(m.values()); |
| } |
| return changes; |
| } else { |
| return new ArrayList<>(modules); |
| } |
| } |
| |
| static class ExternalModuleVisitor implements IModelElementVisitor { |
| final Set<ISourceModule> modules = new HashSet<>(); |
| |
| @Override |
| public boolean visit(IModelElement element) { |
| if (element.getElementType() == IModelElement.SOURCE_MODULE) { |
| if (element instanceof IExternalSourceModule |
| || element instanceof BuiltinSourceModule |
| || ((ISourceModule) element).isBinary()) { |
| modules.add((ISourceModule) element); |
| } |
| return false; |
| } |
| return true; |
| } |
| } |
| |
| private Set<ISourceModule> getExternalSourceModules() |
| throws ModelException { |
| final ExternalModuleVisitor visitor = new ExternalModuleVisitor(); |
| fragment.accept(visitor); |
| return visitor.modules; |
| } |
| |
| @Override |
| public int hashCode() { |
| final int prime = 31; |
| int result = 1; |
| result = prime * result |
| + ((fragment == null) ? 0 : fragment.hashCode()); |
| return result; |
| } |
| |
| @Override |
| public boolean equals(Object obj) { |
| if (this == obj) |
| return true; |
| if (obj == null) |
| return false; |
| if (!super.equals(obj)) |
| return false; |
| if (getClass() != obj.getClass()) |
| return false; |
| ArchiveProjectFragmentRequest other = (ArchiveProjectFragmentRequest) obj; |
| if (fragment == null) { |
| if (other.fragment != null) |
| return false; |
| } else if (!fragment.equals(other.fragment)) |
| return false; |
| return true; |
| } |
| } |