/*=============================================================================# # Copyright (c) 2014-2016 Stephan Wahlbrink (WalWare.de) 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: # Stephan Wahlbrink - initial API and implementation #=============================================================================*/ package de.walware.docmlet.tex.internal.core.builder; import java.util.ArrayList; import java.util.Collection; import java.util.List; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IResourceVisitor; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.SubMonitor; import org.eclipse.core.runtime.content.IContentDescription; import org.eclipse.core.runtime.content.IContentType; import org.eclipse.osgi.util.NLS; import de.walware.ecommons.ltk.IModelManager; import de.walware.ecommons.ltk.ISourceUnitManager; import de.walware.ecommons.ltk.LTK; import de.walware.ecommons.ltk.core.model.ISourceUnit; import de.walware.docmlet.tex.core.TexBuildParticipant; import de.walware.docmlet.tex.core.TexCore; import de.walware.docmlet.tex.core.model.ITexSourceUnit; import de.walware.docmlet.tex.core.model.ITexWorkspaceSourceUnit; import de.walware.docmlet.tex.core.model.LtxSuModelContainer; import de.walware.docmlet.tex.internal.core.TexCorePlugin; import de.walware.docmlet.tex.internal.core.model.LtxModelManager; public class TexProjectBuild extends TexProjectTask implements IResourceVisitor, IResourceDeltaVisitor { private final static class VirtualSourceUnit { private final IFile file; private final String modelTypeId; public VirtualSourceUnit(final IFile file, final String modelTypeId) { this.file= file; this.modelTypeId= modelTypeId; } public IFile getResource() { return this.file; } public String getModelTypeId() { return this.modelTypeId; } @Override public int hashCode() { return this.file.hashCode(); } @Override public String toString() { return this.file.toString(); } } private final ISourceUnitManager suManager= LTK.getSourceUnitManager(); private final MultiStatus status; private final List<ITexWorkspaceSourceUnit> updatedLtxUnits; private final List<VirtualSourceUnit> removedLtxFiles; private SubMonitor visitProgress; public TexProjectBuild(final TexProjectBuilder builder) { super(builder); this.status= new MultiStatus(TexCore.PLUGIN_ID, 0, NLS.bind("TeX build status for ''{0}''", getTexProject().getProject().getName() ), null ); this.updatedLtxUnits= new ArrayList<>(); this.removedLtxFiles= new ArrayList<>(); } private void dispose(final SubMonitor m) { m.setWorkRemaining(this.updatedLtxUnits.size()); for (final ITexSourceUnit unit : this.updatedLtxUnits) { unit.disconnect(m.newChild(1)); } } public void build(final int kind, final SubMonitor m) throws CoreException { try { m.beginTask(NLS.bind("Preparing TeX build for ''{0}''", getTexProject().getProject().getName()), 10 + 20 + 80 + 10 ); final IResourceDelta delta; switch (kind) { case IncrementalProjectBuilder.AUTO_BUILD: case IncrementalProjectBuilder.INCREMENTAL_BUILD: delta= getTexProjectBuilder().getDelta(getTexProject().getProject()); m.worked(10); break; default: delta= null; } if (m.isCanceled()) { throw new CoreException(Status.CANCEL_STATUS); } m.setWorkRemaining(20 + 80 + 10); this.visitProgress= m.newChild(20); if (delta != null) { setBuildType(IncrementalProjectBuilder.INCREMENTAL_BUILD); delta.accept(this); } else { setBuildType(IncrementalProjectBuilder.FULL_BUILD); getTexProject().getProject().accept(this); } this.visitProgress= null; if (m.isCanceled()) { throw new CoreException(Status.CANCEL_STATUS); } processLtxFiles(m.newChild(80, SubMonitor.SUPPRESS_NONE)); } finally { m.setWorkRemaining(10); dispose(m.newChild(10)); if (!this.status.isOK()) { TexCorePlugin.log(this.status); } } } @Override public boolean visit(final IResourceDelta delta) throws CoreException { final IResource resource= delta.getResource(); if (resource.getType() == IResource.FILE) { if (this.visitProgress.isCanceled()) { throw new CoreException(Status.CANCEL_STATUS); } this.visitProgress.setWorkRemaining(100); try { switch (delta.getKind()) { case IResourceDelta.ADDED: case IResourceDelta.CHANGED: visitFileAdded((IFile) resource, delta, this.visitProgress.newChild(1)); break; case IResourceDelta.REMOVED: visitFileRemove((IFile) resource, delta, this.visitProgress.newChild(1)); break; default: break; } } catch (final Exception e) { this.status.add(new Status(IStatus.ERROR, TexCore.PLUGIN_ID, 0, "An error occurred when checking file ''{0}''", e )); } } return true; } @Override public boolean visit(final IResource resource) throws CoreException { if (resource.getType() == IResource.FILE) { this.visitProgress.setWorkRemaining(100); visitFileAdded((IFile) resource, null, this.visitProgress.newChild(1)); } return true; } private void visitFileAdded(final IFile file, final IResourceDelta delta, final SubMonitor m) throws CoreException { final IContentDescription contentDescription= file.getContentDescription(); if (contentDescription == null) { return; } final IContentType contentType= contentDescription.getContentType(); if (contentType == null) { return; } if (contentType.isKindOf(TexCore.LTX_CONTENT_TYPE)) { final ISourceUnit unit= this.suManager.getSourceUnit( LTK.PERSISTENCE_CONTEXT, file, contentType, true, m ); if (unit instanceof ITexWorkspaceSourceUnit) { this.updatedLtxUnits.add((ITexWorkspaceSourceUnit) unit); } else { if (unit != null) { unit.disconnect(m); } clearLtx(file, null); } } } private void visitFileRemove(final IFile file, final IResourceDelta delta, final SubMonitor m) throws CoreException { // There is no contentDescription for removed files // final IContentDescription contentDescription= file.getContentDescription(); // if (contentType.isKindOf(LTX_CONTENT_TYPE)) { // final IModelTypeDescriptor modelType= this.modelRegistry.getModelTypeForContentType(contentType.getId()); // final VirtualSourceUnit unit= new VirtualSourceUnit(file, (modelType != null) ? modelType.getId() : null); // this.removedLtxFiles.add(unit); // // if ((delta != null && (delta.getFlags() & IResourceDelta.MOVED_TO) != 0)) { // final IResource movedTo= file.getWorkspace().getRoot().findMember(delta.getMovedToPath()); // if (movedTo instanceof IFile) { // final TexProject movedToProject= TexProject.getTexProject(movedTo.getProject()); // if (modelType == null // || movedToProject == null || movedToProject == getTexProject() // || !getTexProjectBuilder().hasBeenBuilt(movedToProject.getProject()) ) { // clearLtx((IFile) movedTo, getParticipant(unit.getModelTypeId())); // } // } // } // } } private void processLtxFiles(final SubMonitor m) throws CoreException { m.beginTask(NLS.bind("Analyzing LaTeX file(s) of ''{0}''", getTexProject().getProject().getName()), 2 ); final LtxModelManager ltxModelManager= TexCorePlugin.getInstance().getLtxModelManager(); { final SubMonitor mPart= m.newChild(1); int mRemaining= this.removedLtxFiles.size() + this.updatedLtxUnits.size() * 5; mPart.setWorkRemaining(mRemaining); for (final VirtualSourceUnit unit : this.removedLtxFiles) { try { final TexBuildParticipant participant= getParticipant(unit.getModelTypeId()); // >> remove from LTX index if (participant != null) { participant.ltxUnitRemoved(unit.getResource(), mPart.newChild(1)); } } catch (final Exception e) { this.status.add(new Status(IStatus.ERROR, TexCore.PLUGIN_ID, 0, NLS.bind("An error occurred when processing removed file ''{0}''.", unit.getResource()), e )); } if (mPart.isCanceled()) { throw new CoreException(Status.CANCEL_STATUS); } mPart.setWorkRemaining((mRemaining-= 1)); } if (!this.updatedLtxUnits.isEmpty()) { final LtxBuildReconciler ltxReconciler= new LtxBuildReconciler(ltxModelManager); for (final ITexWorkspaceSourceUnit unit : this.updatedLtxUnits) { try { final TexBuildParticipant participant= getParticipant(unit.getModelTypeId()); clearLtx((IFile) unit.getResource(), participant); final LtxSuModelContainer<ITexSourceUnit> adapter= (LtxSuModelContainer<ITexSourceUnit>) unit.getAdapter(LtxSuModelContainer.class); if (adapter != null) { ltxReconciler.reconcile(adapter, IModelManager.MODEL_FILE, mPart.newChild(3)); if (mPart.isCanceled()) { throw new CoreException(Status.CANCEL_STATUS); } } // >> update LTX index if (participant != null && participant.isEnabled()) { participant.ltxUnitUpdated(unit, mPart.newChild(2)); } } catch (final Exception e) { this.status.add(new Status(IStatus.ERROR, TexCore.PLUGIN_ID, 0, NLS.bind("An error occurred when processing file ''{0}''.", unit.getResource()), e )); } if (mPart.isCanceled()) { throw new CoreException(Status.CANCEL_STATUS); } mPart.setWorkRemaining((mRemaining-= 5)); } } } { final SubMonitor mPart= m.newChild(1); final Collection<TexBuildParticipant> participants= getParticipants(); mPart.setWorkRemaining(participants.size()); for (final TexBuildParticipant participant : participants) { if (participant.isEnabled()) { try { participant.ltxFinished(mPart.newChild(1)); } catch (final Exception e) { this.status.add(new Status(IStatus.ERROR, TexCore.PLUGIN_ID, 0, NLS.bind("An error occurred when processing LaTeX file(s) in ''{0}''.", getTexProject().getProject().getName()), e )); } } } } } private void clearLtx(final IFile file, final TexBuildParticipant partitipant) throws CoreException { if (partitipant != null) { partitipant.clear(file); } } }