/******************************************************************************* * Copyright 2005-2006, CHISEL Group, University of Victoria, Victoria, BC, Canada. * 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: * The Chisel Group, University of Victoria *******************************************************************************/ package net.sourceforge.tagsea.c.waypoints; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.LinkedList; import java.util.List; import net.sourceforge.tagsea.TagSEAPlugin; import net.sourceforge.tagsea.c.CWaypointUtils; import net.sourceforge.tagsea.c.CWaypointsPlugin; import net.sourceforge.tagsea.c.ICWaypointsConstants; import net.sourceforge.tagsea.c.documents.internal.NewWaypointDefinitionExtractor; import net.sourceforge.tagsea.c.resources.internal.WaypointResourceUtil; import net.sourceforge.tagsea.c.waypoints.parser.CWaypointParserFactory; import net.sourceforge.tagsea.c.waypoints.parser.ICWaypointParser; import net.sourceforge.tagsea.c.waypoints.parser.IParsedCWaypointInfo; import net.sourceforge.tagsea.core.ITag; import net.sourceforge.tagsea.core.IWaypoint; import net.sourceforge.tagsea.core.TagSEAOperation; import org.eclipse.core.filebuffers.FileBuffers; import org.eclipse.core.filebuffers.ITextFileBuffer; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IMarker; import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.MultiStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.text.BadLocationException; import org.eclipse.jface.text.DocumentEvent; import org.eclipse.jface.text.IRegion; import org.eclipse.jface.text.Region; class DocumentChangeOperation extends TagSEAOperation { private DocumentEvent event; private ITextFileBuffer filebuffer; public DocumentChangeOperation(DocumentEvent event, ITextFileBuffer fileBuffer) { super("Synchronizing java waypoints to documents..."); this.event = new DocumentEvent(event.getDocument(), event.getOffset(), event.getLength(), event.getText()); if (this.event.fText == null) this.event.fText = ""; this.filebuffer = fileBuffer; } /* (non-Javadoc) * @see net.sourceforge.tagsea.core.ITagSEAOperation#run(org.eclipse.core.runtime.IProgressMonitor) */ public IStatus run(IProgressMonitor monitor) throws InvocationTargetException { IFile file = FileBuffers.getWorkspaceFileAtLocation(filebuffer.getLocation()); MultiStatus status = new MultiStatus(CWaypointsPlugin.PLUGIN_ID, 0, "", null); if (file != null && file.exists()) { IWaypoint[] waypoints = CWaypointsPlugin.getCWaypointsForFile(file); boolean refreshRegion = true; List<IWaypoint> intersectingWaypoints = new LinkedList<IWaypoint>(); for (IWaypoint waypoint : waypoints) { int offset = CWaypointUtils.getOffset(waypoint); int length = CWaypointUtils.getLength(waypoint); int end = offset + length; //adjust the end to the end-of line. This way we can get all the changes. int offsetOfLine; int lineLength; int line; boolean startsLine = false; try { line = event.fDocument.getLineOfOffset(offset); offsetOfLine = event.fDocument.getLineOffset(line); lineLength = event.fDocument.getLineLength(line); String[] delims = event.fDocument.getLegalLineDelimiters(); String delim = ""; for (int i = 0; i < delims.length && !startsLine ; i++) { startsLine = event.fText.startsWith(delims[i]); if (startsLine) delim = delims[i]; } end = offsetOfLine + lineLength - delim.length(); } catch (BadLocationException e) { return updateForNew(file); } if (event.fText == null) event.fText = ""; if (event.fOffset > end || (end == event.fOffset && startsLine)) { if (end == event.fOffset) intersectingWaypoints.add(waypoint); continue; } else if (event.fOffset + event.fLength < offset) { offset += event.fText.length()-event.fLength; int oldLength = CWaypointUtils.getLength(waypoint); CWaypointUtils.setOffset(waypoint, offset); CWaypointUtils.setEnd(waypoint, offset+oldLength); } else if ((event.fOffset >= offset && event.fLength+event.fOffset <= end)){ //the change is contained in a single waypoint. Change only that one. refreshRegion = false; //inside = true; IRegion[] rs = NewWaypointDefinitionExtractor.internalGetWaypointRegions(event.fDocument, offset, end-offset); if (rs == null || rs.length != 1) { // delete the waypoint TagSEAPlugin.getWaypointsModel().removeWaypoint(waypoint); continue; } try { removeProblems(rs[0], file); } catch (CoreException e) { throw new InvocationTargetException(e); } try { String text = event.fDocument.get(rs[0].getOffset(), rs[0].getLength()); ICWaypointParser parser = CWaypointParserFactory.createParser(); IParsedCWaypointInfo info = parser.parse(text, rs[0].getOffset()); status.merge(updateWaypoint(waypoint, info, file)); } catch (BadLocationException e) { throw new InvocationTargetException(e); } } else { if ((event.fOffset >= offset && event.fOffset <= end) || (event.fOffset+event.fLength >= offset && event.fOffset+event.fLength <= end) || (event.fOffset <= offset && event.fOffset+event.fLength >= end)) { //add an intersection intersectingWaypoints.add(waypoint); } } } if (refreshRegion) { //if (!"".equals(event.getText().trim())) //don't care about empty space //refreshForRegion(event.fDocument, event.fOffset, event.fText.length(), event.fLength, file, intersectingWaypoints); status.add(updateForNew(file)); } } return status; } private IStatus updateForNew(IFile file) { class RegionComparator implements Comparator<IRegion> { public int compare(IRegion o1, IRegion o2) { int diff = o1.getOffset() - o2.getOffset(); if (diff == 0) { diff = o1.getLength() - o2.getLength(); } return diff; } } class WaypointComparator implements Comparator<IWaypoint> { public int compare(IWaypoint o1, IWaypoint o2) { int off1 = CWaypointUtils.getOffset(o1); int off2 = CWaypointUtils.getOffset(o2); int diff = off1-off2; if (diff == 0) { int l1 = CWaypointUtils.getLength(o1); int l2 = CWaypointUtils.getLength(o2); diff = l1-l2; } return diff; } } MultiStatus status = new MultiStatus(CWaypointsPlugin.PLUGIN_ID, IStatus.OK, "", null); RegionComparator comparator = new RegionComparator(); IRegion[] newRegions = NewWaypointDefinitionExtractor.getWaypointRegions(event.fDocument); Arrays.sort(newRegions, comparator); ArrayList<IRegion> waypointRegions = new ArrayList<IRegion>(); IWaypoint[] waypoints = CWaypointsPlugin.getCWaypointsForFile(file); for (IWaypoint waypoint : waypoints) { waypointRegions.add(new Region(CWaypointUtils.getOffset(waypoint), CWaypointUtils.getLength(waypoint))); } Collections.sort(waypointRegions, comparator); Arrays.sort(waypoints, new WaypointComparator()); int i = 0; int j = 0; ICWaypointParser parser = CWaypointParserFactory.createParser(); while ((i < newRegions.length) && (j < waypointRegions.size())) { IRegion nr = newRegions[i]; IRegion wr = waypointRegions.get(j); int diff = comparator.compare(nr, wr); if (diff == 0) { i++; j++; } else if (diff < 0){ status.merge(createWaypointForRegion(parser, nr, file)); i++; } else if (diff > 0) { status.merge(waypoints[j].delete().getStatus()); j++; } } while (i < newRegions.length) { IRegion nr = newRegions[i]; status.add(createWaypointForRegion(parser, nr, file)); i++; } while (j < waypoints.length) { status.add(waypoints[j].delete().getStatus()); j++; } return status; } private IStatus createWaypointForRegion(ICWaypointParser parser, IRegion region, IFile file) { try { removeProblems(region, file); String text = event.fDocument.get(region.getOffset(), region.getLength()); WaypointResourceUtil.createWaypointForInfo(parser.parse(text, region.getOffset()), file); } catch (CoreException e) { return e.getStatus(); } catch (BadLocationException e) { return new Status(IStatus.WARNING, CWaypointsPlugin.PLUGIN_ID,IStatus.WARNING, e.getMessage(), e); } return Status.OK_STATUS; } /** * @param waypoint * @param info */ private IStatus updateWaypoint(IWaypoint waypoint, IParsedCWaypointInfo info, IFile file) { if (info.getProblems().length != 0) { WaypointResourceUtil.createProblemsForInfo(info, file); TagSEAPlugin.getWaypointsModel().removeWaypoint(waypoint); return Status.OK_STATUS; } ITag[] tags = waypoint.getTags(); String[] newTags = info.getTags(); Arrays.sort(tags, new Comparator<ITag>(){ public int compare(ITag o1, ITag o2) { return o1.getName().compareTo(o2.getName()); } }); int i = 0; int j = 0; LinkedList<ITag> oldTagsList = new LinkedList<ITag>(); LinkedList<String> newTagsList = new LinkedList<String>(); for (i = 0; i < tags.length && newTags.length != 0; i++) { if (newTags[j].compareTo(tags[i].getName()) < 0) { newTagsList.add(newTags[j]); i--; j++; } else if (newTags[j].compareTo(tags[i].getName()) == 0) { j++; } else { oldTagsList.add(tags[i]); } if (j >= newTags.length) {i++; break;} } for (; i < tags.length; i++) { oldTagsList.add(tags[i]); } for (; j < newTags.length; j++) { newTagsList.add(newTags[j]); } //make sure that the default tag stays in the list MultiStatus status = new MultiStatus(CWaypointsPlugin.PLUGIN_ID, IStatus.OK, "", null); ITag defaultTag = TagSEAPlugin.getTagsModel().getTag(ITag.DEFAULT); oldTagsList.remove(defaultTag); for (ITag old : oldTagsList) { status.add(waypoint.removeTag(old).getStatus()); } for (String newT : newTagsList) { ITag tag = waypoint.addTag(newT); if (tag == null) { status.merge(new Status(IStatus.WARNING, CWaypointsPlugin.PLUGIN_ID, IStatus.WARNING, "Could not add tag " + newT, null)); } } status.merge(CWaypointUtils.setOffset(waypoint, info.getOffset()).getStatus()); status.merge(CWaypointUtils.setEnd(waypoint, info.getOffset() + info.getWaypointData().length()).getStatus()); String author = info.getAuthor(); Date date = info.getDate(); String javaElement = null; if (author != null) { status.merge(waypoint.setAuthor(author).getStatus()); } if (date != null) { status.merge(waypoint.setDate(date).getStatus()); } if (javaElement != null) { //find a fancy way to put in the java element. } status.merge(waypoint.setText(info.getDescription()).getStatus()); return status; } /** * @param rs * @throws CoreException */ private void removeProblems(IRegion region, IFile file) throws CoreException { IMarker[] markers = file.findMarkers(ICWaypointsConstants.WAYPOINT_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); int regionEnd = region.getOffset() + region.getLength(); for (IMarker marker : markers) { int start = marker.getAttribute(IMarker.CHAR_START, -1); int end = marker.getAttribute(IMarker.CHAR_END, -1); if ((start <= region.getOffset() && end <= regionEnd && end >= region.getOffset()) || (start >= region.getOffset() && start <= regionEnd && end >= region.getOffset())) { marker.delete(); } } } }