1717
1818package  walkingkooka .spreadsheet .viewport ;
1919
20+ import  walkingkooka .datetime .DateTimeContexts ;
21+ import  walkingkooka .math .DecimalNumberContexts ;
2022import  walkingkooka .net .HasUrlFragment ;
2123import  walkingkooka .net .UrlFragment ;
24+ import  walkingkooka .spreadsheet .formula .SpreadsheetFormulaParsers ;
25+ import  walkingkooka .spreadsheet .formula .parser .CellSpreadsheetFormulaParserToken ;
26+ import  walkingkooka .spreadsheet .parser .SpreadsheetParserContext ;
27+ import  walkingkooka .spreadsheet .parser .SpreadsheetParserContexts ;
2228import  walkingkooka .spreadsheet .reference .SpreadsheetCellReference ;
2329import  walkingkooka .spreadsheet .reference .SpreadsheetSelection ;
30+ import  walkingkooka .text .CaseSensitivity ;
2431import  walkingkooka .text .CharSequences ;
2532import  walkingkooka .text .CharacterConstant ;
33+ import  walkingkooka .text .cursor .TextCursor ;
34+ import  walkingkooka .text .cursor .TextCursors ;
35+ import  walkingkooka .text .cursor .parser .DoubleParserToken ;
36+ import  walkingkooka .text .cursor .parser .InvalidCharacterExceptionFactory ;
37+ import  walkingkooka .text .cursor .parser .Parser ;
38+ import  walkingkooka .text .cursor .parser .ParserReporters ;
39+ import  walkingkooka .text .cursor .parser .Parsers ;
2640import  walkingkooka .text .printer .IndentingPrinter ;
2741import  walkingkooka .text .printer .TreePrintable ;
42+ import  walkingkooka .tree .expression .ExpressionNumberContexts ;
43+ import  walkingkooka .tree .expression .ExpressionNumberKind ;
2844import  walkingkooka .tree .json .JsonNode ;
2945import  walkingkooka .tree .json .JsonObject ;
3046import  walkingkooka .tree .json .marshall .JsonNodeContext ;
3147import  walkingkooka .tree .json .marshall .JsonNodeMarshallContext ;
3248import  walkingkooka .tree .json .marshall .JsonNodeUnmarshallContext ;
3349
50+ import  java .math .MathContext ;
3451import  java .util .Objects ;
3552
3653/** 
@@ -41,7 +58,9 @@ public final class SpreadsheetViewportRectangle implements Comparable<Spreadshee
4158    TreePrintable ,
4259    HasUrlFragment  {
4360
44-     final  static  CharacterConstant  SEPARATOR  = CharacterConstant .with (':' );
61+     final  static  char  SEPARATOR_CHAR  = ':' ;
62+ 
63+     final  static  CharacterConstant  SEPARATOR  = CharacterConstant .with (SEPARATOR_CHAR );
4564
4665    /** 
4766     * Parses the width and height parse text in the following format. 
@@ -89,6 +108,183 @@ private static double parseDouble(final String token,
89108        }
90109    }
91110
111+     /** 
112+      * Parses the width and height parse text in the following format. 
113+      * <pre> 
114+      * /home/A1/width/200/height/300 
115+      * </pre> 
116+      * Where width and height are decimal numbers. 
117+      */ 
118+     public  static  SpreadsheetViewportRectangle  fromUrlFragment (final  UrlFragment  urlFragment ) {
119+         Objects .requireNonNull (urlFragment , "urlFragment" );
120+ 
121+         final  String  text  = urlFragment .value ();
122+         final  TextCursor  cursor  = TextCursors .charSequence (text );
123+         
124+         SpreadsheetCellReference  home  = null ;
125+         double  width  = 0 ;
126+         double  height  = 0 ;
127+ 
128+         final  int  MODE_SLASH_BEFORE_HOME_TOKEN  = 1 ;
129+         final  int  MODE_HOME_TOKEN  = 2 ;
130+         final  int  MODE_SLASH_BEFORE_CELL_REFERENCE  = 3 ;
131+         final  int  MODE_HOME_TOKEN_CELL_REFERENCE  = 4 ;
132+ 
133+         final  int  MODE_SLASH_BEFORE_WIDTH_TOKEN  = 5 ;
134+         final  int  MODE_WIDTH_TOKEN  = 6 ;
135+         final  int  MODE_SLASH_BEFORE_WIDTH_VALUE  = 7 ;
136+         final  int  MODE_WIDTH_VALUE  = 8 ;
137+ 
138+         final  int  MODE_SLASH_BEFORE_HEIGHT_TOKEN  = 9 ;
139+         final  int  MODE_HEIGHT_TOKEN  = 10 ;
140+         final  int  MODE_SLASH_BEFORE_HEIGHT_VALUE  = 11 ;
141+         final  int  MODE_HEIGHT_VALUE  = 12 ;
142+ 
143+         final  int  MODE_FINISHED  = 13 ;
144+         
145+         int  mode  = MODE_SLASH_BEFORE_HOME_TOKEN ;
146+         while  (cursor .isNotEmpty ()) {
147+             switch  (mode ) {
148+                 case  MODE_SLASH_BEFORE_HOME_TOKEN :
149+                     SLASH_PARSER .parse (cursor , PARSER_CONTEXT );
150+                     mode  = MODE_HOME_TOKEN ;
151+                     break ;
152+                 case  MODE_HOME_TOKEN :
153+                     parseTokenOrFail (cursor , HOME_TOKEN_PARSER , HOME_TOKEN );
154+                     mode  = MODE_SLASH_BEFORE_CELL_REFERENCE ;
155+                     break ;
156+                 case  MODE_SLASH_BEFORE_CELL_REFERENCE :
157+                     SLASH_PARSER .parse (cursor , PARSER_CONTEXT );
158+                     mode  = MODE_HOME_TOKEN_CELL_REFERENCE ;
159+                     break ;
160+                 case  MODE_HOME_TOKEN_CELL_REFERENCE :
161+                     home  = SpreadsheetFormulaParsers .cell ()
162+                         .parse (
163+                             cursor ,
164+                             PARSER_CONTEXT 
165+                         ).orElseThrow (() -> new  IllegalArgumentException ("Missing home" ))
166+                         .cast (CellSpreadsheetFormulaParserToken .class )
167+                         .cell ();
168+                     mode  = MODE_SLASH_BEFORE_WIDTH_TOKEN ;
169+                     break ;
170+ 
171+                 case  MODE_SLASH_BEFORE_WIDTH_TOKEN :
172+                     SLASH_PARSER .parse (cursor , PARSER_CONTEXT );
173+                     mode  = MODE_WIDTH_TOKEN ;
174+                     break ;
175+                 case  MODE_WIDTH_TOKEN :
176+                     parseTokenOrFail (cursor , WIDTH_TOKEN_PARSER , WIDTH_TOKEN );
177+                     mode  = MODE_SLASH_BEFORE_WIDTH_VALUE ;
178+                     break ;
179+                 case  MODE_SLASH_BEFORE_WIDTH_VALUE :
180+                     SLASH_PARSER .parse (cursor , PARSER_CONTEXT );
181+                     mode  = MODE_WIDTH_VALUE ;
182+                     break ;
183+                 case  MODE_WIDTH_VALUE :
184+                     width  = parseDoubleOrFail (
185+                         cursor ,
186+                         WIDTH_TOKEN 
187+                     );
188+                     mode  = MODE_SLASH_BEFORE_HEIGHT_TOKEN ;
189+                     break ;
190+ 
191+                 case  MODE_SLASH_BEFORE_HEIGHT_TOKEN :
192+                     SLASH_PARSER .parse (cursor , PARSER_CONTEXT );
193+                     mode  = MODE_HEIGHT_TOKEN ;
194+                     break ;
195+                 case  MODE_HEIGHT_TOKEN :
196+                     parseTokenOrFail (cursor , HEIGHT_TOKEN_PARSER , HEIGHT_TOKEN );
197+                     mode  = MODE_SLASH_BEFORE_HEIGHT_VALUE ;
198+                     break ;
199+                 case  MODE_SLASH_BEFORE_HEIGHT_VALUE :
200+                     SLASH_PARSER .parse (cursor , PARSER_CONTEXT );
201+                     mode  = MODE_HEIGHT_VALUE ;
202+                     break ;
203+                 case  MODE_HEIGHT_VALUE :
204+                     height  = parseDoubleOrFail (
205+                         cursor ,
206+                         HEIGHT_TOKEN 
207+                     );
208+                     mode  = MODE_FINISHED ;
209+                     break ;
210+ 
211+                 default :
212+                     throw  new  IllegalArgumentException ("Invalid mode: "  + mode );
213+             }
214+         }
215+ 
216+         switch  (mode ) {
217+             case  MODE_SLASH_BEFORE_HOME_TOKEN :
218+             case  MODE_HOME_TOKEN :
219+             case  MODE_SLASH_BEFORE_CELL_REFERENCE :
220+                 throw  new  IllegalArgumentException ("Missing home" );
221+             case  MODE_HOME_TOKEN_CELL_REFERENCE :
222+             case  MODE_SLASH_BEFORE_WIDTH_TOKEN :
223+             case  MODE_WIDTH_TOKEN :
224+             case  MODE_SLASH_BEFORE_WIDTH_VALUE :
225+                 throw  new  IllegalArgumentException ("Missing width" );
226+             case  MODE_WIDTH_VALUE :
227+             case  MODE_SLASH_BEFORE_HEIGHT_TOKEN :
228+             case  MODE_HEIGHT_TOKEN :
229+             case  MODE_SLASH_BEFORE_HEIGHT_VALUE :
230+             case  MODE_HEIGHT_VALUE :
231+                 throw  new  IllegalArgumentException ("Missing height" );
232+             case  MODE_FINISHED :
233+                 return  with (
234+                     home ,
235+                     width ,
236+                     height 
237+                 );
238+             default :
239+                 throw  new  IllegalArgumentException ("Invalid mode: "  + mode );
240+         }
241+     }
242+ 
243+     private  final  static  Parser <SpreadsheetParserContext > SLASH_PARSER  = Parsers .string ("/" , CaseSensitivity .SENSITIVE )
244+         .orReport (ParserReporters .basic ())
245+         .cast ();
246+ 
247+     private  final  static  String  HOME_TOKEN  = "home" ;
248+     private  final  static  Parser <SpreadsheetParserContext > HOME_TOKEN_PARSER  = Parsers .string (HOME_TOKEN , CaseSensitivity .SENSITIVE );
249+ 
250+     private  final  static  String  WIDTH_TOKEN  = "width" ;
251+     private  final  static  Parser <SpreadsheetParserContext > WIDTH_TOKEN_PARSER  = Parsers .string (WIDTH_TOKEN , CaseSensitivity .SENSITIVE );
252+ 
253+     private  final  static  String  HEIGHT_TOKEN  = "height" ;
254+     private  final  static  Parser <SpreadsheetParserContext > HEIGHT_TOKEN_PARSER  = Parsers .string (HEIGHT_TOKEN , CaseSensitivity .SENSITIVE );
255+ 
256+     private  static  void  parseTokenOrFail (final  TextCursor  cursor ,
257+                                          final  Parser <SpreadsheetParserContext > parser ,
258+                                          final  String  label ) {
259+         if (false  == parser .parse (cursor , PARSER_CONTEXT ).isPresent ()) {
260+             throw  new  IllegalArgumentException ("Missing "  + label );
261+         }
262+     }
263+ 
264+     /** 
265+      * Used to parse the width or height values within a {@link UrlFragment}. 
266+      */ 
267+     private  static  double  parseDoubleOrFail (final  TextCursor  cursor ,
268+                                             final  String  label ) {
269+         return  Parsers .doubleParser ()
270+             .parse (
271+                 cursor ,
272+                 PARSER_CONTEXT 
273+             ).orElseThrow (() -> new  IllegalArgumentException ("Missing "  + label ))
274+             .cast (DoubleParserToken .class )
275+             .value ();
276+     }
277+ 
278+     private  final  static  SpreadsheetParserContext  PARSER_CONTEXT  = SpreadsheetParserContexts .basic (
279+         InvalidCharacterExceptionFactory .POSITION_EXPECTED ,
280+         DateTimeContexts .fake (),
281+         ExpressionNumberContexts .basic (
282+             ExpressionNumberKind .BIG_DECIMAL ,
283+             DecimalNumberContexts .american (MathContext .DECIMAL32 )
284+         ),
285+         ';'  // not actually used/ 
286+     );
287+ 
92288    /** 
93289     * Factory that creates a new {@link SpreadsheetViewportRectangle}. 
94290     */ 
0 commit comments