package com.aptana.editor.php.internal.indexer; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.List; import java.util.regex.Pattern; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceRuleFactory; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.IWorkspaceRunnable; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.jobs.ISchedulingRule; import org.eclipse.jface.text.Document; import org2.eclipse.php.internal.core.ast.nodes.Comment; import org2.eclipse.php.internal.core.ast.nodes.Program; import org2.eclipse.php.internal.core.ast.util.Util; import org2.eclipse.php.internal.core.preferences.ITaskTagsListener; import org2.eclipse.php.internal.core.preferences.TaskTagsEvent; import org2.eclipse.php.internal.core.preferences.TaskTagsProvider; import com.aptana.core.logging.IdeLog; import com.aptana.core.resources.TaskTag; import com.aptana.editor.php.PHPEditorPlugin; import com.aptana.editor.php.indexer.PHPGlobalIndexer; import com.aptana.editor.php.internal.builder.BuildPathManager; import com.aptana.editor.php.internal.builder.LocalModule; import com.aptana.editor.php.internal.core.builder.IBuildPath; import com.aptana.editor.php.internal.core.builder.IDirectory; import com.aptana.editor.php.internal.core.builder.IModule; import com.aptana.editor.php.internal.validation.Task; public class TaskTagsUpdater { private static final Pattern NEWLINE_SPLIT = Pattern.compile("\r\n|\r|\n"); //$NON-NLS-1$ // $codepro.audit.disable platformSpecificLineSeparator private static final String APTANA_TASK = "aptana_task"; //$NON-NLS-1$ static { TaskTagsProvider.getInstance().addTaskTagsListener(new ITaskTagsListener() { public void taskCaseChanged(TaskTagsEvent event) { reparse(event); } private void reparse(TaskTagsEvent event) { IProject project = event.getProject(); if (project != null) { IBuildPath buildPathByResource = BuildPathManager.getInstance().getBuildPathByResource(project); if (buildPathByResource != null) { List<IModule> modules = buildPathByResource.getModules(); List<IModule> emptyList = Collections.emptyList(); List<IDirectory> emptyDirList = Collections.emptyList(); PHPGlobalIndexer.getInstance().processChangedAfter(emptyList, modules, emptyList, emptyDirList, emptyDirList); } } else { List<IBuildPath> buildPaths = BuildPathManager.getInstance().getBuildPaths(); List<IModule> lm = new ArrayList<IModule>(); for (IBuildPath p : buildPaths) { lm.addAll(p.getModules()); } List<IModule> emptyList = Collections.emptyList(); List<IDirectory> emptyDirList = Collections.emptyList(); PHPGlobalIndexer.getInstance().processChangedAfter(emptyList, lm, emptyList, emptyDirList, emptyDirList); } } public void taskPrioritiesChanged(TaskTagsEvent event) { reparse(event); } public void taskTagsChanged(TaskTagsEvent event) { reparse(event); } }, null); } void doHandleErrorsJob(Task[] errors, IFile file) { synchronized (this) // prevent simultaneous error updates on the same file { if (ResourcesPlugin.getWorkspace().isTreeLocked()) { IdeLog.logWarning( PHPEditorPlugin.getDefault(), "Error updating the document errors. The workspace tree is locked.", PHPEditorPlugin.DEBUG_SCOPE); //$NON-NLS-1$ } if (file == null || !file.exists()) { return; } int depth = IResource.DEPTH_INFINITE; try { IMarker[] problemMarkers = file.findMarkers(IMarker.TASK, true, depth); for (IMarker m : problemMarkers) { Object attribute2 = m.getAttribute(APTANA_TASK); if (attribute2 != null && attribute2.equals("true")) //$NON-NLS-1$ { m.delete(); } } for (Task t : errors) { IMarker problemMarker = file.createMarker(IMarker.TASK); problemMarker.setAttribute(IMarker.PRIORITY, t.getPriority()); problemMarker.setAttribute(IMarker.CHAR_START, t.getStart()); problemMarker.setAttribute(IMarker.CHAR_END, t.getEnd()); problemMarker.setAttribute(APTANA_TASK, Boolean.TRUE.toString()); problemMarker.setAttribute(IMarker.MESSAGE, t.getDescription()); problemMarker.setAttribute(IMarker.LINE_NUMBER, t.getLineNumber()); } } catch (Exception e) { IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Error updating the PHP task-tags.", e, PHPEditorPlugin.DEBUG_SCOPE); //$NON-NLS-1$ } } } private static ISchedulingRule getMarkerRule(IResource resource) { ISchedulingRule rule = null; if (resource != null) { IResourceRuleFactory ruleFactory = ResourcesPlugin.getWorkspace().getRuleFactory(); rule = ruleFactory.markerRule(resource); } return rule; } public void updateTaskTags(final String contents, final Program program, final List<Comment> comments, IModule module) { try { if (module instanceof LocalModule) { LocalModule lm = (LocalModule) module; final IFile file = lm.getFile(); // TODO get project level task tags/prefs when we support them. Collection<TaskTag> tags = TaskTag.getTaskTags(); boolean isCaseSensitive = TaskTag.isCaseSensitive(); program.setLineEndTable(Util.lineEndTable(new Document(contents))); // visit the comment nodes and parse for tasks! Collection<Task> tasks = new ArrayList<Task>(); for (Comment comment : comments) { tasks.addAll(processCommentNode(program, contents, isCaseSensitive, tags, comment)); } final Task[] finalTasks = tasks.toArray(new Task[tasks.size()]); IWorkspaceRunnable runnable = new IWorkspaceRunnable() { public void run(IProgressMonitor monitor) { doHandleErrorsJob(finalTasks, file); } }; ResourcesPlugin.getWorkspace().run(runnable, getMarkerRule(file), IWorkspace.AVOID_UPDATE, new NullProgressMonitor()); } } catch (Exception e) { IdeLog.logWarning(PHPEditorPlugin.getDefault(), "Error updating the PHP task-tags.", e, PHPEditorPlugin.DEBUG_SCOPE); //$NON-NLS-1$ } } private Collection<Task> processCommentNode(final Program program, final String source, final boolean isCaseSensitive, final Collection<TaskTag> tags, final Comment commentNode) { Collection<Task> tasks = new ArrayList<Task>(); // Explicitly make copies of substrings here and with message to avoid holding ref to underlying char[] for // entire source string (or full line for message)! String text = new String(source.substring(commentNode.getStart(), Math.min(source.length(), commentNode.getEnd() + 1))); if (!isCaseSensitive) { text = text.toLowerCase(); } int offset = 0; String[] lines = NEWLINE_SPLIT.split(text); for (String line : lines) { for (TaskTag entry : tags) { String tag = entry.getName(); if (!isCaseSensitive) { tag = tag.toLowerCase(); } int index = line.indexOf(tag); if (index == -1) { continue; } String message = line.substring(index).trim(); // Remove "**/" from the end of the line! if (message.endsWith("**/")) //$NON-NLS-1$ { message = message.substring(0, message.length() - 3).trim(); } // Remove "*/" from the end of the line! if (message.endsWith("*/")) //$NON-NLS-1$ { message = message.substring(0, message.length() - 2).trim(); } int start = commentNode.getStart() + offset + index; tasks.add(new Task(entry.getName(), new String(message), entry.getPriority(), start, start + line.length() - index, program.getLineNumber(start))); } // FIXME If newline is \r\n, this means we're one off per line in our offsets... offset += line.length() + 1; } return tasks; } }