2021-03-12 18:07:52 +13:00
using System ;
using System.Collections.Generic ;
using System.IO ;
using System.Linq ;
using System.Text ;
using System.Threading.Tasks ;
using System.Globalization ;
using System.Threading ;
2021-04-10 16:13:33 +12:00
using System.Windows.Media.Imaging ;
using Microsoft.Graph ;
using Directory = System . IO . Directory ;
using File = System . IO . File ;
using FileSystemInfo = System . IO . FileSystemInfo ;
2021-03-12 18:07:52 +13:00
namespace Image_Sorter
{
class Program
{
static void Main ( string [ ] args )
{
2021-04-10 16:13:33 +12:00
Worker ( args ) ;
Console . ReadLine ( ) ;
}
public const string MsaClientId = "" ;
//public const string MsaReturnUrl = "urn:ietf:wg:oauth:2.0:oob";
private static GraphServiceClient graphClient { get ; set ; }
// Threading synchronisation
static volatile SemaphoreSlim folderCreateMarshall = new ( 1 ) ;
// Store created folders in a lookup
static volatile Dictionary < string , DriveItem > CreatedFolderItems = new ( ) ;
static readonly List < string > DNGTypes = new List < string > { "raw" , "cr2" , "dng" } ;
static readonly List < string > ImagePrefixes = new List < string > { "" , "Screenshot_" , "VID" } ;
static async void Worker ( string [ ] args )
{
Console . WriteLine ( "Image Sorter {0} (C) 2021 Brychan Dempsey" ) ;
2021-03-12 18:07:52 +13:00
Console . WriteLine ( "Moves images from the source folder and arranges them by date (from metadata) in the destination folder" ) ;
string SourceDir = "" ;
string DestinationDir = "" ;
2021-04-10 16:13:33 +12:00
string OneDriveSourceDir = "" ;
string OneDriveDestinationDir = "" ;
DriveItem OneDriveSourceItem = null ;
DriveItem OneDriveDestItem = null ;
2021-03-12 18:07:52 +13:00
bool copyFiles = false ;
2021-04-10 16:13:33 +12:00
if ( args . Length > 0 & & args [ 0 ] . ToLower ( ) = = "\\s" )
2021-03-12 18:07:52 +13:00
{
int sourceDirSpecified = Array . IndexOf ( args , "\\d" ) ;
if ( sourceDirSpecified ! = - 1 )
{
if ( Directory . Exists ( args [ sourceDirSpecified + 1 ] ) )
{
SourceDir = args [ sourceDirSpecified + 1 ] ;
}
}
int targetDirSpecified = Array . IndexOf ( args , "\\t" ) ;
if ( targetDirSpecified ! = - 1 )
{
if ( Directory . Exists ( args [ targetDirSpecified + 1 ] ) )
{
DestinationDir = args [ targetDirSpecified + 1 ] ;
}
}
else
{
DestinationDir = SourceDir ;
}
if ( args . Contains ( "\\c" ) )
{
copyFiles = true ;
}
// Redirect all console writes
Console . SetOut ( new StringWriter ( ) ) ;
}
else if ( args . Length > 0 & & ( args [ 0 ] = = "\\?" | | args [ 0 ] . ToLower ( ) = = "\\h" ) )
{
Console . WriteLine ( "Image Sorter" ) ;
Console . WriteLine ( "Can be run in silent mode using the argument: \\s" ) ;
Console . WriteLine ( "Additional flags: \\d - Source Directory (Usage: \\s \\d <soruce directory>" ) ;
Console . WriteLine ( "\t\t\\t - Target Directory (Usage: \\s \\t <target directory>" ) ;
Console . WriteLine ( "\t\t\\c - Copy files if this flag is specified" ) ;
Console . WriteLine ( "Usage Example:" ) ;
Console . WriteLine ( "\\s \\d \"C:\\Camera Files\\Images\" \\t \"C:\\Camera Files\\Sorted\\\"" ) ;
Console . WriteLine ( "\nIf the destination directory is not specified, it will default to the source directory" ) ;
Console . ReadLine ( ) ;
Environment . Exit ( 0 ) ;
}
else
{
Console . WriteLine ( "Enter the source directory:" ) ;
SourceDir = Console . ReadLine ( ) ;
Console . WriteLine ( "Enter the destionation directory:" ) ;
DestinationDir = Console . ReadLine ( ) ;
Console . WriteLine ( "Enter \'y\' to perform a copy instead of move" ) ;
string tRead = Console . ReadLine ( ) ;
2021-04-10 16:13:33 +12:00
2021-03-12 18:07:52 +13:00
if ( tRead . Trim ( ) . ToLower ( ) . Equals ( "y" ) )
{
copyFiles = true ;
}
2021-04-10 16:13:33 +12:00
2021-03-12 18:07:52 +13:00
}
Console . WriteLine ( "Scanning the source directory..." ) ;
List < string > SourceFiles = GetFiles ( SourceDir ) ;
2021-04-10 16:13:33 +12:00
Console . WriteLine ( "Found {0} files" , SourceFiles . Count ) ;
string OneDriveLoc = "" ;
//Drive contextDrive = null;
if ( SourceDir . ToLower ( ) . Contains ( "onedrive" ) )
{
OneDriveLoc = SourceDir [ 0. . ( SourceDir . ToLower ( ) . IndexOf ( "onedrive\\" ) + "onedrive\\" . Length ) ] ;
}
2021-03-12 18:07:52 +13:00
2021-04-10 16:13:33 +12:00
bool OneDriveLogin = false ;
if ( ( File . GetAttributes ( SourceDir ) . HasFlag ( FileAttributes . Offline ) | | SourceDir . Contains ( "OneDrive" ) | | SourceFiles . Any ( ( x ) = > File . GetAttributes ( SourceDir + x ) . HasFlag ( FileAttributes . Offline ) ) ) & & DestinationDir . Contains ( OneDriveLoc ) )
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
Console . WriteLine ( "Source and target folders seem to be inside a OneDrive folder.\nWould you like to sign-in to OneDrive and manage files online?" ) ;
string tRead = Console . ReadLine ( ) ;
if ( tRead . Trim ( ) . ToLower ( ) . Equals ( "y" ) )
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
// Do a OneDrive login
if ( SourceDir . ToLower ( ) . Split ( "onedrive" ) . Length > 2 )
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
throw new ArgumentException ( "Cannot identify OneDrive source folder" ) ;
2021-03-12 18:07:52 +13:00
}
2021-04-10 16:13:33 +12:00
try
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
graphClient = AuthenticationHelper . GetAuthenticatedClient ( ) ;
// The user isn't yet logged in - formulate a request:
User me = await graphClient . Me . Request ( ) . GetAsync ( ) ;
//contextDrive = await graphClient.Drives.Request().GetAsync();
Console . WriteLine ( "Authentication of {0} successful. Welcome {1}." , AuthenticationHelper . UserAccount . Username , me . DisplayName ) ;
OneDriveLogin = true ;
OneDriveSourceDir = SourceDir . Replace ( OneDriveLoc , "" ) . Replace ( '\\' , '/' ) ;
OneDriveDestinationDir = DestinationDir . Replace ( OneDriveLoc , "" ) . Replace ( '\\' , '/' ) ;
if ( ! OneDriveSourceDir . StartsWith ( '/' ) )
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
OneDriveSourceDir = '/' + OneDriveSourceDir ;
2021-03-12 18:07:52 +13:00
}
2021-04-10 16:13:33 +12:00
if ( ! OneDriveDestinationDir . StartsWith ( '/' ) )
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
OneDriveDestinationDir = '/' + OneDriveDestinationDir ;
2021-03-12 18:07:52 +13:00
}
2021-04-10 16:13:33 +12:00
// Finally, evaluate the base source and destination folders
OneDriveDestItem = await graphClient . Drive . Root . ItemWithPath ( OneDriveDestinationDir ) . Request ( ) . GetAsync ( ) ;
OneDriveSourceItem = await graphClient . Drive . Root . ItemWithPath ( OneDriveSourceDir ) . Request ( ) . GetAsync ( ) ;
2021-03-12 18:07:52 +13:00
}
2021-04-10 16:13:33 +12:00
catch ( ServiceException exception )
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
Console . WriteLine ( exception ) ;
2021-03-12 18:07:52 +13:00
}
2021-04-10 16:13:33 +12:00
}
}
// Check if the folder is a OneDrive folder
// Do Login if not already complete
int processedCount = 0 ;
object countLock = new ( ) ;
object folderLock = new ( ) ;
Task updateFilesTask = new ( ( ) = >
{
_ = Parallel . ForEach ( SourceFiles , async ( filepath ) = >
{
uint fab = ( uint ) File . GetAttributes ( SourceDir + filepath ) ;
// It seems that FileAttributes.Offline is no longer used to store on-demand OneDrive file status.
// One undefined flag - 1048576 - is set
// As well as defined - 4194304 - FILE_ATTRIBUTE_RECALL_ON_DATA_ACCESS
// We will check initially for the presense of both
bool fileState = ( ( uint ) fab & 1048576 ) = = 1048576 & & ( fab & 4194304 ) = = 4194304 ;
if ( OneDriveLogin & & fileState )
{
// File is online-only, process via OneDrive API
DriveItem file = null ;
DriveItem newFolder = null ;
DriveItem subFolder = null ;
try
{
file = await graphClient . Drive . Root . ItemWithPath ( OneDriveSourceDir + filepath . Replace ( '\\' , '/' ) ) . Request ( ) . GetAsync ( ) ;
}
catch ( ServiceException ex )
{
Console . WriteLine ( ex ) ;
}
// Folder creation will cause a race condition (which modifies the base object),
// Need to intelligently handle this; threads that are simultaneously trying to create the same folder
// must wait; if the folder exists, we don't need to create it.
// Therefore, a semaphore must be used to determine the current state of access
// To handle this smartly, we create a boolean that suggests if the folder is being created (Marshalled by a semaphore)
// The first thread to reach it will set it to true, then check if it exists asynchronously
DateTime fileTime = DateTime . MinValue ;
if ( file . Photo . TakenDateTime ! = null )
{
DateTimeOffset dateTimeOffset = ( DateTimeOffset ) file . Photo . TakenDateTime ;
fileTime = dateTimeOffset . LocalDateTime ;
}
else
{
fileTime = file . FileSystemInfo . CreatedDateTime < file . FileSystemInfo . LastModifiedDateTime ? file . FileSystemInfo . CreatedDateTime . Value . LocalDateTime : file . FileSystemInfo . LastModifiedDateTime . Value . LocalDateTime ;
}
if ( fileTime ! = DateTime . MinValue )
{
// Get the destination folder. If
if ( folderCreateMarshall . CurrentCount > 0 & & CreatedFolderItems . ContainsKey ( fileTime . Year . ToString ( ) ) )
{
newFolder = CreatedFolderItems [ fileTime . Year . ToString ( ) ] ;
}
else
{
// Create a folder object and wait
DriveItem tempNewFolder = new ( )
{
Name = fileTime . Year . ToString ( ) ,
Folder = new Folder ( ) ,
AdditionalData = new Dictionary < string , object >
{
{ "@microsoft.graph.conflictBehavior" , "fail" }
}
} ;
// Wait for our turn to create the object
await folderCreateMarshall . WaitAsync ( ) ;
try
{
// Check the folder wasn't surprise created
if ( CreatedFolderItems . ContainsKey ( fileTime . Year . ToString ( ) ) )
{
newFolder = CreatedFolderItems [ fileTime . Year . ToString ( ) ] ;
}
else
{
Console . WriteLine ( "Creating Folder" ) ;
// Folder doesn't exist or wasn't added; create and add
newFolder = await graphClient . Drive . Root . ItemWithPath ( OneDriveDestinationDir )
. Children . Request ( ) . AddAsync ( tempNewFolder ) ;
CreatedFolderItems . Add ( fileTime . Year . ToString ( ) , newFolder ) ;
}
}
catch ( ServiceException ex )
{
// TODO: Handle the existing item
if ( ex . StatusCode = = System . Net . HttpStatusCode . Conflict )
{
try
{
newFolder = await graphClient . Drive . Root . ItemWithPath ( OneDriveDestinationDir + "/" + fileTime . Year . ToString ( ) ) . Request ( ) . GetAsync ( ) ;
CreatedFolderItems . Add ( fileTime . Year . ToString ( ) , newFolder ) ;
}
catch ( ServiceException exNF )
{
Console . WriteLine ( exNF ) ;
}
}
else
{
Console . WriteLine ( ex ) ;
}
}
catch ( Exception ex )
{
Console . WriteLine ( ex ) ;
}
finally
{
folderCreateMarshall . Release ( ) ;
}
}
if ( folderCreateMarshall . CurrentCount > 0 & & CreatedFolderItems . ContainsKey ( fileTime . Year . ToString ( ) + "/" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) ) )
{
subFolder = CreatedFolderItems [ fileTime . Year . ToString ( ) + "/" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) ] ;
}
else
{
// Create a folder and wait
DriveItem tempNewFolder = new ( )
{
Name = CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) ,
Folder = new Folder ( ) ,
AdditionalData = new Dictionary < string , object >
{
{ "@microsoft.graph.conflictBehavior" , "fail" }
}
} ;
await folderCreateMarshall . WaitAsync ( ) ;
try
{
// Check the folder wasn't surprise created
if ( CreatedFolderItems . ContainsKey ( fileTime . Year . ToString ( ) + "/" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) ) )
{
subFolder = CreatedFolderItems [ fileTime . Year . ToString ( ) + "/" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) ] ;
}
else
{
Console . WriteLine ( "Creating Subfolder" ) ;
// Folder doesn't exist or wasn't added; create and add
subFolder = await graphClient . Drive . Root . ItemWithPath ( OneDriveDestinationDir + "/" + fileTime . Year . ToString ( ) )
. Children . Request ( ) . AddAsync ( tempNewFolder ) ;
CreatedFolderItems . Add ( fileTime . Year . ToString ( ) + "/" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) , subFolder ) ;
}
}
catch ( ServiceException ex )
{
if ( ex . StatusCode = = System . Net . HttpStatusCode . Conflict )
{
try
{
subFolder = await graphClient . Drive . Root . ItemWithPath ( OneDriveDestinationDir + "/" + fileTime . Year . ToString ( ) + "/" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) ) . Request ( ) . GetAsync ( ) ;
CreatedFolderItems . Add ( fileTime . Year . ToString ( ) + "/" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( fileTime . Month ) , subFolder ) ;
}
catch ( ServiceException exNF )
{
Console . WriteLine ( exNF ) ;
}
}
else
{
Console . WriteLine ( ex ) ;
}
}
finally
{
folderCreateMarshall . Release ( ) ;
}
}
// Finally, get the resulting files
ItemReference parentReference = new ( )
{
Id = subFolder . Id
} ;
try
{
// Fail-fast, copy item first
if ( await graphClient . Me . Drive . Items [ file . Id ] . Copy ( null , parentReference ) . Request ( ) . PostAsync ( ) ! = file & & ! copyFiles )
{
// Only if the returned file is not a duplicate, and we aren't copying, shall we remove the old file
await graphClient . Drive . Items [ file . Id ] . Request ( ) . DeleteAsync ( ) ;
}
}
catch ( ServiceException ex )
{
Console . WriteLine ( ex ) ;
}
}
}
else
{
string fileDestination = "" ;
DateTime photoDate = new ( ) ;
bool hasMetadata = true ;
using ( FileStream fs = new ( SourceDir + filepath , FileMode . Open , FileAccess . Read , FileShare . Read ) )
{
// If the file is online-only, allow a large amount of time to download
if ( fileState )
{
fs . ReadTimeout = ( int ) TimeSpan . FromMinutes ( 60 ) . TotalMilliseconds ; // Allow about 60 minutes for a download; should be enough for even large concurrent downloads
}
bool ruleMatch = false ;
Dictionary < string , string > imgMetadata = GetMetaData ( fs ) ;
if ( imgMetadata . ContainsKey ( "date" ) )
{
photoDate = DateTime . Parse ( imgMetadata [ "date" ] ) ;
ruleMatch = true ;
}
// If the file is a negative, it may have a conjugate image file
else if ( DNGTypes . Contains ( Path . GetExtension ( filepath ) . TrimStart ( '.' ) ) )
{
List < string > matches = SourceFiles . FindAll ( ( x ) = > Path . GetFileNameWithoutExtension ( x ) . Equals ( Path . GetFileNameWithoutExtension ( SourceDir + filepath ) ) & & ! Path . GetExtension ( x ) . Equals ( Path . GetExtension ( SourceDir + filepath ) ) ) ;
if ( matches . Count = = 1 )
{
using ( FileStream tfs = new ( matches [ 0 ] , FileMode . Open , FileAccess . Read , FileShare . Read ) )
{
imgMetadata = GetMetaData ( tfs ) ;
}
if ( imgMetadata . ContainsKey ( "date" ) )
{
photoDate = DateTime . Parse ( imgMetadata [ "date" ] ) ;
Console . WriteLine ( " Conjugate file found - " + Path . GetFileName ( SourceDir + filepath ) ) ;
ruleMatch = true ;
}
}
}
// Try parsing a date by the rules set in ImagePrefixes, using the first if found
else if ( ImagePrefixes . Exists ( ( x ) = > TryParseDate ( Path . GetFileNameWithoutExtension ( SourceDir + filepath ) . TrimStart ( x . ToCharArray ( ) ) , out photoDate ) ) )
{
Console . WriteLine ( " Date successfully parsed from file: {0} - Parsed Date: {1} " , Path . GetFileName ( SourceDir + filepath ) , photoDate . ToString ( "dd-MM-yyyy" ) ) ;
ruleMatch = true ;
}
// Failsafe to avoid implausable dates (Greater than system year + 10, < 1990)
if ( ruleMatch & & ( photoDate . Year > DateTime . Now . Year + 10 | | photoDate . Year < 1990 ) )
{
ruleMatch = false ;
}
// Finally, resort to the least of the file creation time && file modified time
if ( ! ruleMatch )
{
FileSystemInfo fileInfo = new FileInfo ( SourceDir + filepath ) ;
photoDate = fileInfo . CreationTime < fileInfo . LastWriteTime ? fileInfo . CreationTime : fileInfo . LastWriteTime ;
}
}
fileDestination = "\\" + photoDate . Year ;
fileDestination + = "\\" + CultureInfo . CurrentCulture . DateTimeFormat . GetMonthName ( photoDate . Month ) ;
Directory . CreateDirectory ( DestinationDir + fileDestination + "\\" ) ;
fileDestination + = "\\" + filepath [ filepath . LastIndexOf ( '\\' ) . . ] ;
if ( copyFiles )
{
try
{
File . Copy ( SourceDir + filepath , DestinationDir + fileDestination ) ;
filepath = DestinationDir + fileDestination ;
}
catch
{
Console . WriteLine ( "Failed to copy {0}" , Path . GetFileName ( SourceDir + filepath ) ) ;
}
}
else
{
try
{
File . Move ( SourceDir + filepath , DestinationDir + fileDestination ) ;
filepath = DestinationDir + fileDestination ;
}
catch
{
Console . WriteLine ( "Failed to move {0}" , Path . GetFileName ( SourceDir + filepath ) ) ;
}
}
}
lock ( countLock )
{
processedCount + + ;
}
} ) ;
2021-03-12 18:07:52 +13:00
} ) ;
int lastProcessed = 0 ;
updateFilesTask . Start ( ) ;
bool eval = false ;
while ( ! eval )
{
lock ( countLock )
{
if ( processedCount ! = lastProcessed )
{
Console . WriteLine ( "{0}/{1} processed." , processedCount , SourceFiles . Count ) ;
lastProcessed = processedCount ;
}
if ( processedCount = = SourceFiles . Count )
eval = true ;
}
Thread . Sleep ( 10 ) ;
}
Console . ReadLine ( ) ;
}
2021-04-10 16:13:33 +12:00
static Dictionary < string , string > GetMetaData ( FileStream fileStream )
2021-03-12 18:07:52 +13:00
{
2021-04-10 16:13:33 +12:00
2021-03-12 18:07:52 +13:00
Dictionary < string , string > Metadata = new Dictionary < string , string > ( ) ;
try
{
BitmapSource srcimg = BitmapFrame . Create ( fileStream ) ;
BitmapMetadata md = ( BitmapMetadata ) srcimg . Metadata ;
string date = md . DateTaken ;
if ( ! string . IsNullOrEmpty ( date ) )
{
Metadata . Add ( "date" , date ) ;
}
}
catch
{
Console . WriteLine ( "Couldn't get metadata - {0}" , fileStream . Name ) ;
}
return Metadata ;
}
2021-04-10 16:13:33 +12:00
2021-03-12 18:07:52 +13:00
static List < string > GetFiles ( string SourceDirectory )
{
2021-04-10 16:13:33 +12:00
// Get the files, remove the source
// This gives us a source list with relative pathing. This allows OneDrive files to be easily incorporated
List < string > foundFiles = Directory . GetFiles ( SourceDirectory , "*" , SearchOption . TopDirectoryOnly ) . Select ( ( s ) = > s . Replace ( SourceDirectory , "" ) ) . ToList ( ) ;
2021-03-12 18:07:52 +13:00
foreach ( var subDirectory in Directory . GetDirectories ( SourceDirectory ) )
{
foundFiles . AddRange ( GetFiles ( subDirectory ) ) ;
}
return foundFiles ;
}
static bool TryParseDate ( string input , out DateTime dt )
{
// Attempt a base conversion first
// As datetime's built-in converter does not seem to be able to handle more complex strings,
// convert the to a usable format before attempting to convert a datetime
//int pos = ImagePrefixes.FindIndex(0, (x) => input.StartsWith(x));
//input = input.Replace(ImagePrefixes[pos], ""); // Remove the prefix
if ( DateTime . TryParse ( input , out dt ) )
return true ;
StringBuilder converted = new StringBuilder ( ) ;
string modified = input ;
DateTime result = new DateTime ( ) ;
// if we are in the format "ddddsddsdd" we can try to parse that date
if ( isMatch ( input ) )
{
int year = Convert . ToInt32 ( input . Substring ( 0 , 4 ) ) ;
int month = Convert . ToInt32 ( input . Substring ( 5 , 2 ) ) ;
int day = Convert . ToInt32 ( input . Substring ( 8 , 2 ) ) ;
try
{
result = new DateTime ( year , month , day ) ;
}
catch
{
return false ;
}
}
// Else try the straight digit parsing, so ensure we have 8 contiguous digits to parse.
else if ( isMatch ( input , "dddddddd" ) )
{
int year = Convert . ToInt32 ( input . Substring ( 0 , 4 ) ) ;
int month = Convert . ToInt32 ( input . Substring ( 4 , 2 ) ) ;
int day = Convert . ToInt32 ( input . Substring ( 6 , 2 ) ) ;
try
{
result = new DateTime ( year , month , day ) ;
}
catch
{
return false ;
}
}
2021-04-10 16:13:33 +12:00
if ( result . Year < = DateTime . Now . Year + 10 & & result . Year > = 1980 )
2021-03-12 18:07:52 +13:00
{
// Falls within an acceptable date range, so try use it as a date
dt = result ;
return true ;
}
return false ;
}
private static bool isNumeric ( char c1 )
{
if ( c1 > = 48 & & c1 < 58 )
{
return true ;
}
else return false ;
}
private static bool isSymbol ( char c1 )
{
if ( ( c1 > = 32 & & c1 < 48 ) | | ( c1 > = 58 & & c1 < 65 ) | | ( c1 > = 91 & & c1 < 97 ) | | ( c1 > = 123 & & c1 < 127 ) )
{
return true ;
}
else return false ;
}
/// <summary>
/// Checks if the provided string matches the format specified
/// d = digit
/// a = alphanumeric
/// s = symbol
/// * = any
/// </summary>
/// <param name="source"></param>
/// <param name="format"></param>
/// <returns></returns>
private static bool isMatch ( string source , string format = "ddddsddsdd" )
{
if ( source . Length < format . Length ) return false ;
bool state = true ;
for ( int i = 0 ; i < format . Length ; i + + )
{
if ( format [ i ] = = 'd' )
{
if ( ! isNumeric ( source [ i ] ) )
{
return false ;
}
}
else if ( format [ i ] = = 's' )
{
if ( ! isSymbol ( source [ i ] ) )
{
return false ;
}
}
}
return state ;
}
}
}