1 /*
2  * Hunt - A refined core library for D programming language.
3  *
4  * Copyright (C) 2018-2019 HuntLabs
5  *
6  * Website: https://www.huntlabs.net/
7  *
8  * Licensed under the Apache-2.0 License.
9  *
10  */
11 
12 module hunt.text.Pattern;
13 
14 import hunt.text.Common;
15 import hunt.text.StringUtils;
16 
17 import std.string;
18 
19 
20 abstract class Pattern {
21 	
22 	private __gshared AllMatch ALL_MATCH;
23 
24     shared static this() {
25         ALL_MATCH = new AllMatch();
26     }
27 	
28 	/**
29 	 * Matches a string according to the specified pattern
30 	 * @param str Target string
31 	 * @return If it returns null, that represents matching failure, 
32 	 * else it returns an array contains all strings are matched.
33 	 */
34 	abstract string[] match(string str);
35 	
36 	static Pattern compile(string pattern, string wildcard) {
37 		bool startWith = pattern.startsWith(wildcard);
38 		bool endWith = pattern.endsWith(wildcard);
39 		string[] array = StringUtils.split(pattern, wildcard);
40 		
41 		switch (array.length) {
42 		case 0:
43 			return ALL_MATCH;
44 		case 1:
45 			if (startWith && endWith)
46 				return new HeadAndTailMatch(array[0]);
47 			
48 			if (startWith)
49 				return new HeadMatch(array[0]);
50 			
51 			if (endWith)
52 				return new TailMatch(array[0]);
53 			
54 			return new EqualsMatch(pattern);
55 		default:
56 			return new MultipartMatch(startWith, endWith, array);
57 		}
58 	}
59 	
60 	
61 	private static class MultipartMatch : Pattern {
62 		
63 		private bool startWith, endWith;
64 		private string[] parts;
65 		private int num;
66 
67 		this(bool startWith, bool endWith, string[] parts) {
68 			// super();
69 			this.startWith = startWith;
70 			this.endWith = endWith;
71 			this.parts = parts;
72 			num = cast(int)parts.length - 1;
73 			if(startWith)
74 				num++;
75 			if(endWith)
76 				num++;
77 		}
78 
79 		override
80 		string[] match(string str) {
81 			int currentIndex = -1;
82 			int lastIndex = -1;
83 			string[] ret = new string[num];
84 			
85 			for (int i = 0; i < parts.length; i++) {
86 				string part = parts[i];
87 				int j = startWith ? i : i - 1;
88 				currentIndex = cast(int)str.indexOf(part, lastIndex + 1);
89 				
90 				if (currentIndex > lastIndex) {
91 					if(i != 0 || startWith)
92 						ret[j] = str.substring(lastIndex + 1, currentIndex);
93 					
94 					lastIndex = currentIndex + cast(int)part.length - 1;
95 					continue;
96 				}
97 				return null;
98 			}
99 			
100 			if(endWith)
101 				ret[num - 1] = str.substring(lastIndex + 1);
102 			
103 			return ret;
104 		}
105 		
106 	}
107 	
108 	private static class TailMatch : Pattern {
109 		private string part;
110 
111 		this(string part) {
112 			this.part = part;
113 		}
114 
115 		override
116 		string[] match(string str) {
117 			int currentIndex = cast(int)str.indexOf(part);
118 			if(currentIndex == 0) {
119 				return [str.substring(cast(int)part.length)];
120 			}
121 			return null;
122 		}
123 	}
124 	
125 	private static class HeadMatch : Pattern {
126 		private string part;
127 
128 		this(string part) {
129 			this.part = part;
130 		}
131 
132 		override
133 		string[] match(string str) {
134 			int currentIndex = cast(int)str.indexOf(part);
135 			if(currentIndex + part.length == str.length) {
136 				return [str.substring(0, currentIndex)];
137 			}
138 			return null;
139 		}
140 		
141 		
142 	}
143 	
144 	private static class HeadAndTailMatch : Pattern {
145 		private string part;
146 
147 		this(string part) {
148 			this.part = part;
149 		}
150 
151 		override
152 		string[] match(string str) {
153 			int currentIndex = cast(int)str.indexOf(part);
154 			if(currentIndex >= 0) {
155 				string[] ret = [str[0 .. currentIndex],
156 						str.substring(currentIndex + cast(int)part.length, cast(int)str.length) ];
157 				return ret;
158 			}
159 			return null;
160 		}
161 		
162 		
163 	}
164 	
165 	private static class EqualsMatch : Pattern {
166 		private string pattern;
167 		
168 		this(string pattern) {
169 			this.pattern = pattern;
170 		}
171 
172 		override
173 		string[] match(string str) {
174 			return pattern.equals(str) ? new string[0] : null;
175 		}
176 	}
177 	
178 	private static class AllMatch : Pattern {
179 
180 		override
181 		string[] match(string str) {
182 			return [str];
183 		}
184 	}
185 }