1 /+
2 The MIT License (MIT)
3 
4     Copyright (c) <2013> <Oleg Butko (deviator), Anton Akzhigitov (Akzwar)>
5 
6     Permission is hereby granted, free of charge, to any person obtaining a copy
7     of this software and associated documentation files (the "Software"), to deal
8     in the Software without restriction, including without limitation the rights
9     to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10     copies of the Software, and to permit persons to whom the Software is
11     furnished to do so, subject to the following conditions:
12 
13     The above copyright notice and this permission notice shall be included in
14     all copies or substantial portions of the Software.
15 
16     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17     IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18     FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19     AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20     LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21     OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22     THE SOFTWARE.
23 +/
24 
25 module des.math.util.accessstring;
26 
27 import std.string;
28 import std.algorithm;
29 import std.stdio;
30 
31 /// construct valid value access strings
32 string arrayAccessStringCtor( string sep1, string sep2, string[][] variants... ) pure
33 in
34 {
35     assert( sep1 != sep2 );
36     assert( variants.length > 0 );
37     auto l = variants[0].length;
38     assert( all!(a=>a.length == l)( variants ) );
39 }
40 body
41 {
42     string[] rr;
43     foreach( var; variants )
44         rr ~= var.join(sep1);
45     return rr.join(sep2);
46 }
47 
48 ///
49 unittest
50 {
51     enum s1 = " ", s2 = "|";
52     static assert( isCompatibleArrayAccessStrings( 2, arrayAccessStringCtor( s1, s2, ["x","y"], ["alpha","beta"] ), s1, s2 ) );
53 }
54 
55 /// compatible for creating access dispatches
56 pure bool isCompatibleArrayAccessStrings( size_t N, string str, string sep1="", string sep2="|" )
57 in { assert( sep1 != sep2 ); } body
58 {
59     auto strs = str.split(sep2);
60     foreach( s; strs )
61         if( !isCompatibleArrayAccessString(N,s,sep1) )
62             return false;
63 
64     string[] fa;
65     foreach( s; strs )
66         fa ~= s.split(sep1);
67 
68     foreach( ref v; fa ) v = strip(v);
69 
70     foreach( i, a; fa )
71         foreach( j, b; fa )
72             if( i != j && a == b ) return false;
73 
74     return true;
75 }
76 
77 
78 /// compatible for creating access dispatches
79 pure bool isCompatibleArrayAccessString( size_t N, string str, string sep="" )
80 { return N == getAccessFieldsCount(str,sep) && isArrayAccessString(str,sep); }
81 
82 ///
83 pure bool isArrayAccessString( in string as, in string sep="", bool allowDot=false )
84 {
85     if( as.length == 0 ) return false;
86     auto splt = as.split(sep);
87     foreach( i, val; splt )
88         if( !isValueAccessString(val,allowDot) || canFind(splt[0..i],val) )
89             return false;
90     return true;
91 }
92 
93 ///
94 pure size_t getAccessFieldsCount( string str, string sep )
95 { return str.split(sep).length; }
96 
97 ///
98 pure ptrdiff_t getIndex( string as, string arg, string sep1="", string sep2="|" )
99 in { assert( sep1 != sep2 ); } body
100 {
101     foreach( str; as.split(sep2) )
102         foreach( i, v; str.split(sep1) )
103             if( arg == v ) return i;
104     return -1;
105 }
106 
107 ///
108 pure bool oneOfAccess( string str, string arg, string sep="" )
109 {
110     auto splt = str.split(sep);
111     return canFind(splt,arg);
112 }
113 
114 ///
115 pure bool oneOfAccessAll( string str, string arg, string sep="" )
116 {
117     auto splt = arg.split("");
118     return all!(a=>oneOfAccess(str,a,sep))(splt);
119 }
120 
121 ///
122 pure bool oneOfAnyAccessAll( string str, string arg, string sep1="", string sep2="|" )
123 in { assert( sep1 != sep2 ); } body
124 {
125     foreach( s; str.split(sep2) )
126         if( oneOfAccessAll(s,arg,sep1) ) return true;
127     return false;
128 }
129 
130 /// check symbol count for access to field
131 pure bool isOneSymbolPerFieldForAnyAccessString( string str, string sep1="", string sep2="|" )
132 in { assert( sep1 != sep2 ); } body
133 {
134     foreach( s; str.split(sep2) )
135         if( isOneSymbolPerFieldAccessString(s,sep1) ) return true;
136     return false;
137 }
138 
139 /// check symbol count for access to field
140 pure bool isOneSymbolPerFieldAccessString( string str, string sep="" )
141 {
142     foreach( s; str.split(sep) )
143         if( s.length > 1 ) return false;
144     return true;
145 }
146 
147 /++
148 Using all functions
149 +/
150 unittest
151 {
152     static assert(  isValueAccessString( "hello" ) );
153     static assert(  isValueAccessString( "x" ) );
154     static assert(  isValueAccessString( "_ok" ) );
155     static assert(  isValueAccessString( "__ok" ) );
156     static assert(  isValueAccessString( "__o1k" ) );
157     static assert(  isValueAccessString( "_2o1k3" ) );
158     static assert( !isValueAccessString( "0__ok" ) );
159     static assert( !isValueAccessString( "__o-k" ) );
160     static assert(  isArrayAccessString( "xyz" ) );
161     static assert(  isArrayAccessString( "x|dx|y|dy", "|" ) );
162     static assert(  isCompatibleArrayAccessString( 4, "x|dx|y|dy", "|" ) );
163     static assert(  isCompatibleArrayAccessString( 3, "xyz" ) );
164     static assert( !isCompatibleArrayAccessString( 4, "xxxy" ) );
165     static assert( !isCompatibleArrayAccessString( 3, "xxx" ) );
166     static assert(  isCompatibleArrayAccessStrings( 3, "xyz" ) );
167     static assert(  isCompatibleArrayAccessStrings( 3, "x y z", " " ) );
168     static assert(  isCompatibleArrayAccessStrings( 2, "xy|uv" ) );
169     static assert(  isCompatibleArrayAccessStrings( 3, "abc|efg" ) );
170     static assert( !isCompatibleArrayAccessStrings( 3, "abc|afg" ) );
171     static assert( !isCompatibleArrayAccessStrings( 3, "xxy|uv" ) );
172     static assert(  isCompatibleArrayAccessStrings( 3, "x,y,z;u,v,t", ",", ";" ) );
173     static assert( getIndex( "x y z", "x", " " ) == 0 );
174     static assert( getIndex( "x y z", "y", " " ) == 1 );
175     static assert( getIndex( "x y z", "z", " " ) == 2 );
176     static assert( getIndex( "x|dx|y|dy", "dx", "|", ";" ) == 1 );
177     static assert( getIndex( "x|dx|y|dy", "1dx", "|", ";" ) == -1 );
178 
179     static assert( oneOfAccessAll("xyz","xy") );
180     static assert( oneOfAccessAll("xyz","yx") );
181     static assert( oneOfAccessAll("xyz","xxxxyxyyyz") );
182     static assert( oneOfAccessAll("x,y,z","xxxxyxyyyz",",") );
183     static assert( isOneSymbolPerFieldAccessString("xyz") );
184     static assert( isOneSymbolPerFieldAccessString("x,y,z",",") );
185     static assert( isOneSymbolPerFieldForAnyAccessString( "xy|uv", "", "|" ) );
186     static assert( isOneSymbolPerFieldForAnyAccessString( "near far|n f", " ", "|" ) );
187 
188     static assert( !isArrayAccessString("x.y.z","",false) );
189     static assert( !isArrayAccessString("x.y.z","",true) );
190     static assert( !isArrayAccessString("x.y.z"," ",false) );
191     static assert(  isArrayAccessString("x.y.z"," ",true) );
192     static assert(  isArrayAccessString("pos.x pos.y pos.z vel.x vel.y vel.z"," ",true) );
193 
194     static assert(  isArrayAccessString( "pos vel", " ", true ) );
195     static assert(  isArrayAccessString( "abcd", " ", true ) );
196     static assert(  isArrayAccessString( "a1 a2", " ", true ) );
197     static assert(  isArrayAccessString( "ok.no", " ", true ) );
198     auto fstr = "pos.x pos.y vel.x vel.y";
199     assert(  isArrayAccessString( fstr, " ", true ) );
200     assert( !isArrayAccessString( fstr[0 .. $-1], " ", true ) );
201     assert( !isArrayAccessString( "ok.1", " ", true ) );
202     assert( !isArrayAccessString( "1abcd", " ", true ) );
203     assert( !isArrayAccessString( "not 2ok", " ", true ) );
204 }
205 
206 pure
207 {
208 
209 bool isValueAccessString( in string as, bool allowDot=false )
210 {
211     return as.length > 0 &&
212     startsWithAllowedChars(as) &&
213     (allowDot?(all!(a=>isValueAccessString(a))(as.split("."))):allowedCharsOnly(as));
214 }
215 
216 bool startsWithAllowedChars( in string as )
217 {
218     switch(as[0])
219     {
220         case 'a': .. case 'z': goto case;
221         case 'A': .. case 'Z': goto case;
222         case '_': return true;
223         default: return false;
224     }
225 }
226 
227 bool allowedCharsOnly( in string as )
228 {
229     foreach( c; as ) if( !allowedChar(c) ) return false;
230     return true;
231 }
232 
233 bool allowedChar( in char c )
234 {
235     switch(c)
236     {
237         case 'a': .. case 'z': goto case;
238         case 'A': .. case 'Z': goto case;
239         case '0': .. case '9': goto case;
240         case '_': return true;
241         default: return false;
242     }
243 }
244 
245 }