|
8 | 8 | * it under the terms of the GNU General Public License as |
9 | 9 | * published by the Free Software Foundation, either version 3 of the |
10 | 10 | * License, or (at your option) any later version. |
11 | | - * |
| 11 | + * |
12 | 12 | * This program is distributed in the hope that it will be useful, |
13 | 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | 15 | * GNU General Public License for more details. |
16 | | - * |
| 16 | + * |
17 | 17 | * You should have received a copy of the GNU General Public |
18 | 18 | * License along with this program. If not, see |
19 | 19 | * <http://www.gnu.org/licenses/gpl-3.0.html>. |
@@ -1476,17 +1476,28 @@ private void writePathToXML(final PrintWriter pw, final Path p) { |
1476 | 1476 | String startsString = ""; |
1477 | 1477 | String endsString = ""; |
1478 | 1478 | if (p.parentPath != null) { |
1479 | | - final int startPathID = p.parentPath.getID(); |
1480 | | - // Find the nearest index for backward compatibility: |
1481 | | - int nearestIndexOnStartPath = -1; |
1482 | | - if (p.parentPath.size() > 0) { |
1483 | | - nearestIndexOnStartPath = p.parentPath.indexNearestTo(p.getBranchPoint().x, |
1484 | | - p.getBranchPoint().y, p.getBranchPoint().z); |
| 1479 | + // Guard: only write startson if the parent exists in the manager. |
| 1480 | + // A dangling parentPath (e.g. from a stale clone reference after deletion) |
| 1481 | + // would produce a startson ID that cannot be resolved on load, causing an NPE |
| 1482 | + // see https://github.com/morphonets/SNT/issues/270 |
| 1483 | + if (getPathFromID(p.parentPath.getID()) == null) { |
| 1484 | + SNTUtils.log("Warning: path " + p.getID() + " ('" + p.getName() + "') has a parentPath (ID " |
| 1485 | + + p.parentPath.getID() + ") not registered in the manager. " |
| 1486 | + + "The branch relationship will not be saved to avoid a corrupt file."); |
| 1487 | + p.parentPath = null; // repair in-memory to keep state consistent |
| 1488 | + } else { |
| 1489 | + final int startPathID = p.parentPath.getID(); |
| 1490 | + // Find the nearest index for backward compatibility: |
| 1491 | + int nearestIndexOnStartPath = -1; |
| 1492 | + if (p.parentPath.size() > 0) { |
| 1493 | + nearestIndexOnStartPath = p.parentPath.indexNearestTo(p.getBranchPoint().x, |
| 1494 | + p.getBranchPoint().y, p.getBranchPoint().z); |
| 1495 | + } |
| 1496 | + startsString = " startson=\"" + startPathID + "\"" + " startx=\"" + p.getBranchPoint().x + "\"" |
| 1497 | + + " starty=\"" + p.getBranchPoint().y + "\"" + " startz=\"" + p.getBranchPoint().z + "\""; |
| 1498 | + if (nearestIndexOnStartPath >= 0) |
| 1499 | + startsString += " startsindex=\"" + nearestIndexOnStartPath + "\""; |
1485 | 1500 | } |
1486 | | - startsString = " startson=\"" + startPathID + "\"" + " startx=\"" + p.getBranchPoint().x + "\"" |
1487 | | - + " starty=\"" + p.getBranchPoint().y + "\"" + " startz=\"" + p.getBranchPoint().z + "\""; |
1488 | | - if (nearestIndexOnStartPath >= 0) |
1489 | | - startsString += " startsindex=\"" + nearestIndexOnStartPath + "\""; |
1490 | 1501 | } |
1491 | 1502 | if (p.isPrimary()) |
1492 | 1503 | pw.print(" primary=\"true\""); |
@@ -2000,41 +2011,52 @@ public void endElement(final String uri, final String localName, |
2000 | 2011 |
|
2001 | 2012 | if (startID != null) { |
2002 | 2013 | final Path startPath = getPathFromID(startID); |
2003 | | - if (startJoinPoint == null) { |
2004 | | - // Then we have to get it from startIndexInteger: |
2005 | | - startJoinPoint = startPath.getNodeWithoutChecks(startIndexInteger); |
| 2014 | + if (startPath == null) { |
| 2015 | + SNTUtils.log("Malformed traces file: startson=" + startID |
| 2016 | + + " referenced by path " + p.getID() + " ('" + p.getName() |
| 2017 | + + "') does not exist. Branch relationship skipped."); |
| 2018 | + } else { |
| 2019 | + if (startJoinPoint == null) { |
| 2020 | + // Then we have to get it from startIndexInteger: |
| 2021 | + startJoinPoint = startPath.getNodeWithoutChecks(startIndexInteger); |
| 2022 | + } |
| 2023 | + p.setBranchFrom(startPath, startJoinPoint); |
2006 | 2024 | } |
2007 | | - p.setBranchFrom(startPath, startJoinPoint); |
2008 | 2025 | } |
2009 | 2026 | if (endID != null) { |
2010 | 2027 | Path endPath = getPathFromID(endID); |
2011 | | - if (endJoinPoint == null) { |
2012 | | - // Then we have to get it from endIndexInteger: |
2013 | | - endJoinPoint = endPath.getNodeWithoutChecks(endIndexInteger); |
2014 | | - } |
2015 | | - // We no longer support end point joining, so we'll reverse everything |
2016 | | - // and apply a 'start join'; |
2017 | | - SNTUtils.log("End joining no longer supported: Establishing a star-join' on reversed path"); |
2018 | | - Path pReversed = p.reversed(); |
2019 | | - // replace the path in all data structures... |
2020 | | - allPaths.remove(p); |
2021 | | - pathIdMap.remove(p.getID(), p); |
2022 | | - pathNameMap.remove(p.getName(), p); |
2023 | | - |
2024 | | - allPaths.add(pReversed); |
2025 | | - pathIdMap.put(pReversed.getID(), pReversed); |
2026 | | - pathNameMap.put(pReversed.getName(), pReversed); |
2027 | | - |
2028 | | - pReversed.setBranchFrom(endPath, endJoinPoint); |
2029 | | - |
2030 | | - for (final Path join : new ArrayList<>(p.connectedPaths)) { |
2031 | | - if (join.getParentPath() == p) { |
2032 | | - final PointInImage joinPoint = join.getBranchPoint(); |
2033 | | - join.detachFromParent(); |
2034 | | - join.setBranchFrom(pReversed, joinPoint); |
| 2028 | + if (endPath == null) { |
| 2029 | + SNTUtils.log("Malformed traces file: endson=" + endID |
| 2030 | + + " referenced by path " + p.getID() + " ('" + p.getName() |
| 2031 | + + "') does not exist. End-join skipped."); |
| 2032 | + } else { |
| 2033 | + if (endJoinPoint == null) { |
| 2034 | + // Then we have to get it from endIndexInteger: |
| 2035 | + endJoinPoint = endPath.getNodeWithoutChecks(endIndexInteger); |
2035 | 2036 | } |
2036 | | - } |
2037 | | - |
| 2037 | + // We no longer support end point joining, so we'll reverse everything |
| 2038 | + // and apply a 'start join'; |
| 2039 | + SNTUtils.log("End joining no longer supported: Establishing a star-join' on reversed path"); |
| 2040 | + Path pReversed = p.reversed(); |
| 2041 | + // replace the path in all data structures... |
| 2042 | + allPaths.remove(p); |
| 2043 | + pathIdMap.remove(p.getID(), p); |
| 2044 | + pathNameMap.remove(p.getName(), p); |
| 2045 | + |
| 2046 | + allPaths.add(pReversed); |
| 2047 | + pathIdMap.put(pReversed.getID(), pReversed); |
| 2048 | + pathNameMap.put(pReversed.getName(), pReversed); |
| 2049 | + |
| 2050 | + pReversed.setBranchFrom(endPath, endJoinPoint); |
| 2051 | + |
| 2052 | + for (final Path join : new ArrayList<>(p.connectedPaths)) { |
| 2053 | + if (join.getParentPath() == p) { |
| 2054 | + final PointInImage joinPoint = join.getBranchPoint(); |
| 2055 | + join.detachFromParent(); |
| 2056 | + join.setBranchFrom(pReversed, joinPoint); |
| 2057 | + } |
| 2058 | + } |
| 2059 | + } // end null-check else |
2038 | 2060 | } |
2039 | 2061 | if (fittedID != null) { |
2040 | 2062 | p.fitted = getPathFromID(fittedID); |
|
0 commit comments