Index: applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/AlignWaysCmdKeepAngles.java
===================================================================
--- applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/AlignWaysCmdKeepAngles.java	(revision 27587)
+++ applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/AlignWaysCmdKeepAngles.java	(revision 27588)
@@ -34,8 +34,11 @@
 
         // Now the calculatedNodes reflect the coordinates that we'd have
-        // without preserving the angles.
+        // without preserving the angles, i.e. preserving the length.
 
         Map<Node,EastNorth> calcNodesKeepLength = calculatedNodes;
 
+        // Now we'll proceed with the hypothetical recalculation of the endpoint coordinates
+        // following the rule of preserving angles instead. The new nodes will be stored in nodeArr[].
+        
         Node[] nodeArr = algnSeg.getSegmentEndPoints().toArray(new Node[2]);
 
@@ -44,35 +47,95 @@
         AlignWaysGeomLine lineKeepLength = new AlignWaysGeomLine(enCalc1.getX(), enCalc1.getY(), enCalc2.getX(), enCalc2.getY());
 
-        recalculateNodes(lineKeepLength, nodeArr[0]);
-        recalculateNodes(lineKeepLength, nodeArr[1]);
-
-    }
-
-
-    void recalculateNodes(AlignWaysGeomLine alignedLineKeepLength, Node endpoint) {
-
+        recalculateNodesAndValidate(lineKeepLength, nodeArr[0]);
+        recalculateNodesAndValidate(lineKeepLength, nodeArr[1]);
+
+    }
+
+
+    void recalculateNodesAndValidate(AlignWaysGeomLine alignedLineKeepLength, Node endpoint) {
+
+        if (endpoint.getEastNorth().equals(pivot)) {
+        	// endpoint is pivot: the coordinates won't change
+        	return;
+        }
+        
         ArrayList<WaySegment> alws = algnSeg.getAdjacentWaySegments(endpoint);
-        if (alws.size() == 1) {
+        if (alws.size() <= 2) {
             // We need the intersection point of
-            //  - the freely rotated alignee as calculated in calcNodesFreeAngle
+            //  - the alignee following the keep length rule
             //  - the adjacent way segment
-
-            EastNorth enAdj1 = alws.get(0).getFirstNode().getEastNorth();
-            EastNorth enAdj2 = alws.get(0).getSecondNode().getEastNorth();
-
-            // Update the calculated node for aligning while keeping angles
-            AlignWaysGeomPoint isectPnt = alignedLineKeepLength.getIntersection(new AlignWaysGeomLine(enAdj1.getX(), enAdj1.getY(), enAdj2.getX(), enAdj2.getY()));
+        	
+        	EastNorth enAdjOther1 = getNonEqualEN(alws.get(0), endpoint);
+        	EastNorth enAdjOther2 = null;
+
+            if (alws.size() == 2) {
+            	enAdjOther2 = getNonEqualEN(alws.get(1), endpoint);
+            	
+            	// In order have a chance to align, (enAdjOther1, enAdjOther2 and endpoint) must be collinear
+            	ArrayList<EastNorth> enAdjPts = new ArrayList<EastNorth>(3);
+            	enAdjPts.add(enAdjOther1);
+            	enAdjPts.add(endpoint.getEastNorth());
+            	enAdjPts.add(enAdjOther2);
+            	if (!isEnSetCollinear(enAdjPts)) {
+            		// Not collinear, no point to proceed
+                    alignableStatKeepAngles = AlignableStatus.ALGN_INV_ANGLE_PRESERVING_CONFLICT;
+                    return;
+            	}
+            	
+            }
+
+            // Update the calculated node for angle preserving alignment
+            AlignWaysGeomPoint isectPnt = alignedLineKeepLength.getIntersection(new AlignWaysGeomLine(enAdjOther1.getX(), enAdjOther1.getY(), 
+            																						  endpoint.getEastNorth().getX(), endpoint.getEastNorth().getY()));
+            EastNorth enIsectPt = null;
             // If the intersection is null, the adjacent and the alignee are parallel already:
             // there's no need to update this node
             if (isectPnt != null) {
-                calculatedNodes.put(endpoint, new EastNorth(isectPnt.getX(), isectPnt.getY()));
+            	enIsectPt = new EastNorth(isectPnt.getX(), isectPnt.getY());       	
+                calculatedNodes.put(endpoint, enIsectPt);
             } else if (alignedLineKeepLength.getIntersectionStatus() == IntersectionStatus.LINES_PARALLEL) {
                 alignableStatKeepAngles = AlignableStatus.ALGN_INV_ANGLE_PRESERVING_CONFLICT;
             }
+            
+            // For the case of two adjacent segments with collinear points, the new endpoint may  
+            // not fall between enAdjOther1 and enAdjOther2; in this case one of them is redundant 
+            // and should be deleted from OSM
+            if (alws.size() == 2 && enIsectPt != null) {
+            	int middlePtIdx = AlignWaysGeomPoint.getMiddleOf3(
+            			new AlignWaysGeomPoint(enIsectPt), 
+            			new AlignWaysGeomPoint(enAdjOther1), 
+            			new AlignWaysGeomPoint(enAdjOther2));
+            	if (middlePtIdx != 0) {
+            		EastNorth middlePt = null;
+            		switch(middlePtIdx) {
+            			case 1:
+            				middlePt = enIsectPt;
+            				break;
+            			case 2:
+            				middlePt = enAdjOther1;
+            				break;
+            			case 3:
+            				middlePt = enAdjOther2;
+            				break;
+            		}
+	            	if (middlePt != null) {
+	            		double eps = 1E-6;
+	                	if (!middlePt.equalsEpsilon(enIsectPt, eps)) {
+	                		// Intersection point didn't fall between the two adjacent points; something must go 
+	                		if (middlePt.equalsEpsilon(enAdjOther1, eps)) {
+	                			// TODO Delete enAdjOther1
+	                			if (true);
+	                			// Main.map.
+	                		} else
+	                			// TODO Delete enAdjOther2
+	                			if (true);
+	                	}
+	            	}
+            	}
+            }
+            
         } else {
-            if (!endpoint.getEastNorth().equals(pivot)) {
-                // Report non-pivot endpoints only
-                alignableStatKeepAngles = AlignableStatus.ALGN_INV_TOOMANY_CONNECTED_WS;
-            }
+        	// angle preserving alignment not possible
+            alignableStatKeepAngles = AlignableStatus.ALGN_INV_TOOMANY_CONNECTED_WS;
         }
 
@@ -80,5 +143,27 @@
 
 
-    /**
+    private boolean isEnSetCollinear(ArrayList<EastNorth> enAdjPts) {
+    	ArrayList<AlignWaysGeomPoint> awAdjPts = new ArrayList<AlignWaysGeomPoint>();
+    	
+    	for (EastNorth en : enAdjPts) {
+    		AlignWaysGeomPoint pt = new AlignWaysGeomPoint(en.getX(), en.getY());
+    		awAdjPts.add(pt);
+    	}
+    	
+		return AlignWaysGeomPoint.isSetCollinear(awAdjPts);
+	}
+
+
+	private EastNorth getNonEqualEN(WaySegment waySegment, Node endpoint) {
+    	if (waySegment.getFirstNode().equals(endpoint)) {
+    		return waySegment.getSecondNode().getEastNorth();
+    	} else if (waySegment.getSecondNode().equals(endpoint)) {
+    		return waySegment.getFirstNode().getEastNorth();
+    	} else
+    		return null;
+	}
+
+
+	/**
      * Reports invalid alignable statuses on screen in dialog boxes.
      * 
@@ -140,16 +225,5 @@
             return status;
 
-        // Check the remainder of the potential problems: -
-        // Check for the number of segments connecting to the alignee endpoints
-        for (Node nA : displaceableNodes) {
-            if (nA.getEastNorth().equals(pivot)) {
-                // Pivots set to endpoints are exempt from this check
-                continue;
-            } else {
-                // This node should not connect to more than one other way segment
-                if (isReferredByNOtherWaySegments(nA, 2))
-                    return AlignableStatus.ALGN_INV_TOOMANY_CONNECTED_WS;
-            }
-        }
+        // Check the remainder of the potential problems: N/A
 
         // In all other cases alignment is possible
Index: applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomLine.java
===================================================================
--- applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomLine.java	(revision 27587)
+++ applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomLine.java	(revision 27588)
@@ -75,6 +75,10 @@
     }
 
-    /**
-     * Returns the intersection point the line with another line.
+    public AlignWaysGeomLine(AlignWaysGeomPoint awPt1, AlignWaysGeomPoint awPt2) {
+    	this(awPt1.getX(), awPt1.getY(), awPt2.getX(), awPt2.getY());
+	}
+
+	/**
+     * Returns the intersection point of the line with another line.
      * If the lines are parallel or overlap, returns null.
      * Use getIntersectionStatus() to determine the case.
@@ -180,3 +184,23 @@
     }
 
+	public boolean isPointOnLine(AlignWaysGeomPoint awPt) {
+		// Method:
+		// 1. create a new line from awPt and one point of 'this'
+		// 2. check getIntersectionStatus of the two lines
+		// 3. if status is LINES_OVERLAP, the point os one the line, otherwise not
+		
+		// Need an arbitrary point on this line; let it be (x, y)
+		Double x = 0.0;
+		Double y = getYonLine(x);
+		if (y.isNaN()) y = 0.0;
+		
+		AlignWaysGeomLine line2 = new AlignWaysGeomLine(awPt, new AlignWaysGeomPoint(x, y));
+		getIntersection(line2);
+		if (getIntersectionStatus() == IntersectionStatus.LINES_OVERLAP)
+			return true;
+		else
+			return false;
+		
+	}
+
 }
Index: applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomPoint.java
===================================================================
--- applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomPoint.java	(revision 27587)
+++ applications/editors/josm/plugins/alignways/src/com/tilusnet/josm/plugins/alignways/geometry/AlignWaysGeomPoint.java	(revision 27588)
@@ -1,3 +1,9 @@
 package com.tilusnet.josm.plugins.alignways.geometry;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+
+import org.openstreetmap.josm.data.coor.EastNorth;
 
 public class AlignWaysGeomPoint {
@@ -10,5 +16,10 @@
     }
 
-    public double getX() {
+    public AlignWaysGeomPoint(EastNorth eastNorth) {
+    	this.x = eastNorth.getX();
+    	this.y = eastNorth.getY();
+	}
+
+	public double getX() {
         return x;
     }
@@ -26,3 +37,81 @@
     }
 
+	public static boolean isSetCollinear(ArrayList<AlignWaysGeomPoint> awPts) {
+		if (awPts.size() <= 1)
+			return false;
+		
+		if (awPts.size() == 2)
+			return true;
+		else {
+			// at least 3 points
+			// First create a line of the first two points in the set
+			AlignWaysGeomLine line = new AlignWaysGeomLine(awPts.get(0), awPts.get(1));
+			// ...then check the subsequent points whether they are on the line
+			for (int i = 2; i < awPts.size(); i++) {
+				if (!line.isPointOnLine(awPts.get(i))) {
+					return false;
+				}
+			}
+			return true;
+		}
+	}
+
+	/**
+	 * Determines which (EastNorth) point falls between the other two.
+	 * Ideally to be used with collinear points.
+	 * 
+	 * @return 1, 2 or 3 for pt1, pt2 and pt3, respectively. 
+	 * 0 if middle value cannot be determined (i.e. some values are equal). 
+	 */
+	public static int getMiddleOf3(
+			AlignWaysGeomPoint pt1, 
+			AlignWaysGeomPoint pt2, 
+			AlignWaysGeomPoint pt3) {
+		
+		int midPtXIdx = getMiddleOf3(pt1.x, pt2.x, pt3.x);
+		int midPtYIdx = getMiddleOf3(pt1.y, pt2.y, pt3.y);
+		
+		if ((midPtXIdx == 0) && (midPtYIdx == 0))
+			// All 3 points overlap: 
+			// Design decision: return the middle point (could be any other or none)
+			return 2;
+		
+		if (midPtXIdx == 0) return midPtYIdx; 
+		if (midPtYIdx == 0) return midPtXIdx;
+		
+		// Both x and y middle points could be determined;
+		// their indexes must coincide
+		if (midPtXIdx == midPtYIdx)
+			// Success
+			return midPtXIdx; // (or midPtYIdx)
+		else
+			// Fail
+			return 0;
+		
+	}
+
+	/**
+	 * Determine which value, d1, d2 or d3 falls in the middle of the other two.
+	 * @return 1, 2 or 3 for d1, d2 and d3, respectively. 
+	 * 0 if middle value cannot be determined (i.e. some values are equal). 
+	 */
+	private static int getMiddleOf3(double d1, double d2, double d3) {
+		
+		Double[] dValues = {d1, d2, d3};
+		ArrayList<Double> alValues = new ArrayList<Double>(Arrays.asList(dValues));
+		Collections.sort(alValues);
+		
+		if ((Math.abs(alValues.get(1) - alValues.get(0)) < 0.01) ||
+		    (Math.abs(alValues.get(1) - alValues.get(2)) < 0.01))
+			// Cannot determine absolute middle value
+			return 0;
+		else {
+			if (Math.abs(alValues.get(1) - d1) < 0.01) return 1;
+			if (Math.abs(alValues.get(1) - d2) < 0.01) return 2;
+			if (Math.abs(alValues.get(1) - d3) < 0.01) return 3;
+		}
+		
+		// Should never happen
+		return 0;
+	}
 }
