2017-09-23 10:08:18 +02:00
|
|
|
|
using System;
|
2017-09-15 18:22:19 +02:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Umbraco.Core.Persistence.Repositories
|
|
|
|
|
|
{
|
|
|
|
|
|
internal class SimilarNodeName
|
|
|
|
|
|
{
|
|
|
|
|
|
private int _numPos = -2;
|
|
|
|
|
|
|
|
|
|
|
|
public int Id { get; set; }
|
2017-11-07 19:49:14 +01:00
|
|
|
|
public string Text { get; set; }
|
2017-09-15 18:22:19 +02:00
|
|
|
|
|
|
|
|
|
|
// cached - reused
|
|
|
|
|
|
public int NumPos
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_numPos != -2) return _numPos;
|
|
|
|
|
|
|
2017-11-07 19:49:14 +01:00
|
|
|
|
var name = Text;
|
2017-09-15 18:22:19 +02:00
|
|
|
|
|
|
|
|
|
|
if (name[name.Length - 1] != ')')
|
|
|
|
|
|
return _numPos = -1;
|
|
|
|
|
|
|
|
|
|
|
|
var pos = name.LastIndexOf('(');
|
|
|
|
|
|
if (pos < 2 || pos == name.Length - 2) // < 2 and not < 0, because we want at least "x ("
|
|
|
|
|
|
return _numPos = -1;
|
|
|
|
|
|
|
|
|
|
|
|
return _numPos = pos;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// not cached - used only once
|
|
|
|
|
|
public int NumVal
|
|
|
|
|
|
{
|
|
|
|
|
|
get
|
|
|
|
|
|
{
|
|
|
|
|
|
if (NumPos < 0)
|
|
|
|
|
|
throw new InvalidOperationException();
|
|
|
|
|
|
int num;
|
2017-11-07 19:49:14 +01:00
|
|
|
|
if (int.TryParse(Text.Substring(NumPos + 1, Text.Length - 2 - NumPos), out num))
|
2017-09-15 18:22:19 +02:00
|
|
|
|
return num;
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// compare without allocating, nor parsing integers
|
|
|
|
|
|
internal class Comparer : IComparer<SimilarNodeName>
|
|
|
|
|
|
{
|
|
|
|
|
|
public int Compare(SimilarNodeName x, SimilarNodeName y)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (x == null) throw new ArgumentNullException("x");
|
|
|
|
|
|
if (y == null) throw new ArgumentNullException("y");
|
|
|
|
|
|
|
|
|
|
|
|
var xpos = x.NumPos;
|
|
|
|
|
|
var ypos = y.NumPos;
|
|
|
|
|
|
|
2017-11-07 19:49:14 +01:00
|
|
|
|
var xname = x.Text;
|
|
|
|
|
|
var yname = y.Text;
|
2017-09-15 18:22:19 +02:00
|
|
|
|
|
|
|
|
|
|
if (xpos < 0 || ypos < 0 || xpos != ypos)
|
|
|
|
|
|
return string.Compare(xname, yname, StringComparison.Ordinal);
|
|
|
|
|
|
|
|
|
|
|
|
// compare the part before (number)
|
|
|
|
|
|
var n = string.Compare(xname, 0, yname, 0, xpos, StringComparison.Ordinal);
|
|
|
|
|
|
if (n != 0)
|
|
|
|
|
|
return n;
|
|
|
|
|
|
|
|
|
|
|
|
// compare (number) lengths
|
|
|
|
|
|
var diff = xname.Length - yname.Length;
|
|
|
|
|
|
if (diff != 0) return diff < 0 ? -1 : +1;
|
|
|
|
|
|
|
|
|
|
|
|
// actually compare (number)
|
|
|
|
|
|
var i = xpos;
|
|
|
|
|
|
while (i < xname.Length - 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (xname[i] != yname[i])
|
|
|
|
|
|
return xname[i] < yname[i] ? -1 : +1;
|
|
|
|
|
|
i++;
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// gets a unique name
|
|
|
|
|
|
public static string GetUniqueName(IEnumerable<SimilarNodeName> names, int nodeId, string nodeName)
|
|
|
|
|
|
{
|
|
|
|
|
|
var uniqueNumber = 1;
|
|
|
|
|
|
var uniqueing = false;
|
|
|
|
|
|
foreach (var name in names.OrderBy(x => x, new Comparer()))
|
|
|
|
|
|
{
|
|
|
|
|
|
// ignore self
|
|
|
|
|
|
if (nodeId != 0 && name.Id == nodeId) continue;
|
|
|
|
|
|
|
|
|
|
|
|
if (uniqueing)
|
|
|
|
|
|
{
|
2017-11-07 19:49:14 +01:00
|
|
|
|
if (name.NumPos > 0 && name.Text.StartsWith(nodeName) && name.NumVal == uniqueNumber)
|
2017-09-15 18:22:19 +02:00
|
|
|
|
uniqueNumber++;
|
|
|
|
|
|
else
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
2017-11-07 19:49:14 +01:00
|
|
|
|
else if (name.Text.InvariantEquals(nodeName))
|
2017-09-15 18:22:19 +02:00
|
|
|
|
{
|
|
|
|
|
|
uniqueing = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return uniqueing ? string.Concat(nodeName, " (", uniqueNumber.ToString(), ")") : nodeName;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2017-09-23 10:08:18 +02:00
|
|
|
|
}
|