1414# Jython tools don't require sys.path modification
1515
1616from wlsdeploy .aliases .aliases import Aliases
17+ from wlsdeploy .aliases .wlst_modes import WlstModes
1718from wlsdeploy .exception import exception_helper
1819from wlsdeploy .logging .platform_logger import PlatformLogger
1920from wlsdeploy .tool .modelhelp .model_help_printer import ModelHelpPrinter
2021from wlsdeploy .tool .modelhelp .model_help_utils import ControlOptions
2122from wlsdeploy .tool .util import model_context_helper
2223from wlsdeploy .util import cla_helper
24+ from wlsdeploy .util import model
2325from wlsdeploy .util .cla_utils import CommandLineArgUtil
2426from wlsdeploy .util .exit_code import ExitCode
2527
3436__optional_arguments = [
3537 CommandLineArgUtil .ATTRIBUTES_ONLY_SWITCH ,
3638 CommandLineArgUtil .FOLDERS_ONLY_SWITCH ,
37- CommandLineArgUtil .RECURSIVE_SWITCH
39+ CommandLineArgUtil .RECURSIVE_SWITCH ,
40+ CommandLineArgUtil .INTERACTIVE_MODE_SWITCH
3841]
3942
4043__output_types = [
@@ -70,6 +73,314 @@ def __process_args(args):
7073 return model_context_helper .create_context (_program_name , argument_map )
7174
7275
76+ def canonical_path_from_model_path (model_path ):
77+ """
78+ helper function for interactive help
79+ canonicalize a model path so it:
80+ - has no ':'
81+ - always starts with '/'
82+ - does not end with a slash unless at top
83+ - converts 'top' to '/'
84+ - note: a standalone '/' implies "top"
85+ - note: first string after first '/' is normally a section
86+ :param model_path: the model path
87+ :return: canonicalized path
88+ """
89+ ret_path = model_path .replace (':' ,'' )
90+ while ret_path .endswith ('/' ):
91+ ret_path = ret_path [:- 1 ]
92+ while ret_path .startswith ('/' ):
93+ ret_path = ret_path [1 :]
94+ if ret_path == 'top' :
95+ ret_path = ''
96+ ret_path = '/' + ret_path
97+ return ret_path
98+
99+
100+ def model_path_from_canonical_path (canonical_path ):
101+ """
102+ helper function for interactive help
103+ returns "normal" model path based on canonicalized path
104+ :param canonical_path: the path in "/.../..." format
105+ :return: the model path with "section:/..." format or "top"
106+ """
107+ ret_path = canonical_path [1 :]
108+ slashpos = ret_path .find ('/' )
109+ if slashpos > 0 :
110+ ret_path = ret_path [0 :slashpos ] + ':' + ret_path [slashpos :]
111+ if not ret_path :
112+ ret_path = 'top'
113+ return ret_path
114+
115+
116+ def parse_dir_command_simple (model_path , command_str ):
117+ """
118+ helper function to process interactive help commands 'cd ..', 'cd', 'top', or 'ls'
119+ :param model_path: the starting model path before the command
120+ :param command_str: the command
121+ :return: the resulting path (an absolute canonical path)
122+ """
123+
124+ canonical_path = canonical_path_from_model_path (model_path )
125+
126+ if command_str == 'cd ..' :
127+ new_path = canonical_path [:canonical_path .rfind ('/' )]
128+ if not new_path :
129+ return '/'
130+ else :
131+ return new_path
132+
133+ if command_str == 'ls' :
134+ return canonical_path
135+
136+ # must be 'top' or 'cd'
137+ return '/'
138+
139+ def add_section_if_missing (aliases , canonical_path , token ):
140+ """
141+ helper function to try find and prepend section if missing
142+ :param aliases: aliases
143+ :param canonical_path: candidate canonical path starting with "/"
144+ :param token: first token in canonical_path
145+ :return: potentially updated canonical path
146+ """
147+
148+ if token not in model .get_model_top_level_keys ():
149+ for section_name in model .get_model_top_level_keys ():
150+ if token in aliases .get_model_section_top_level_folder_names (section_name ):
151+ return '/' + section_name + canonical_path
152+
153+ return canonical_path
154+
155+ def parse_dir_command_cd_path (aliases , model_path , command_str ):
156+ """
157+ helper function to process interactive help command 'cd [path]'
158+ :param aliases: aliases
159+ :param model_path: the starting model path before the command
160+ :param command_str: the 'cd' command
161+ :return: the resulting path (an absolute canonical path)
162+ """
163+
164+ print ("Parsing" )
165+
166+ ret_path = command_str [3 :].replace (':' ,'' ).strip ()
167+ while ret_path .endswith ('/' ):
168+ ret_path = ret_path [:- 1 ]
169+ while ret_path .replace ('//' ,'/' ) != ret_path :
170+ ret_path = ret_path .replace ('//' ,'/' )
171+ if ret_path .startswith ('top/' ):
172+ ret_path = ret_path [3 :]
173+ while ret_path .startswith ('/top/' ):
174+ ret_path = ret_path [4 :]
175+
176+ if not ret_path or ret_path == 'top' or ret_path == '/' :
177+ return '/'
178+
179+ tokens = ret_path .split ('/' )
180+
181+ if tokens [0 ] in model .get_model_top_level_keys ():
182+ # if first token is a section name, make it an absolute path
183+ return '/' + ret_path
184+
185+ if ret_path .startswith ('/' ):
186+ # user specified an absolute path '/token1[/...]'
187+ # (starts with '/' so there must be a second token (tokens[1]))
188+ return add_section_if_missing (aliases , ret_path , tokens [1 ])
189+
190+ # this is a relative path, so append it to the current path
191+
192+ canonical_path = canonical_path_from_model_path (model_path )
193+
194+ if canonical_path == "/" :
195+ return "/" + ret_path
196+
197+ return canonical_path + "/" + ret_path
198+
199+
200+ def parse_dir_command_all (aliases , model_path , command_str ):
201+ """
202+ helper function to process interactive help commands 'cd [path]', 'cd ..', 'cd', 'top', or 'ls'
203+ :param aliases: aliases
204+ :param model_path: the starting model path before the command
205+ :param command_str: the command
206+ :return: the resulting path (an absolute canonical path)
207+ """
208+
209+ if command_str == 'cd ..' or command_str == 'cd' or command_str == 'top' or command_str == 'ls' :
210+ return parse_dir_command_simple (model_path , command_str )
211+ else :
212+ return parse_dir_command_cd_path (aliases , model_path , command_str ) # handle 'cd [path]'
213+
214+
215+ def interactive_help_prompt (model_path , input_file ):
216+ """
217+ Gets the next command from stdin or a file.
218+ :param model_path: a current model path
219+ :param input_file: specify a file to get input from file instead of stdin.
220+ :param printer: a model help printer
221+ :return: returns when user types 'exit'
222+ """
223+
224+ # prompt using sys.stdout.write to avoid newline
225+ sys .stdout .write ("[" + model_path + "] --> " )
226+ sys .stdout .flush ()
227+
228+ if not input_file :
229+ command_str = raw_input ("" ) # get command from stdin
230+
231+ else :
232+ # get command from file instead of stdin (undocumented feature)
233+ command_str = input_file .readline ()
234+ if not command_str :
235+ command_str = 'exit' # reached EOF
236+ else :
237+ command_str = command_str .rstrip (os .linesep )
238+
239+ # show retrieved command_str right after the prompt
240+ print (command_str )
241+
242+ command_str = " " .join (command_str .split ()) # remove extra white-space
243+ return command_str
244+
245+
246+ def interactive_help_print_path (printer , model_path , history ):
247+ """
248+ Prints help for the given model_path, or an error message.
249+ Also updates the help history on success.
250+ :param model_path: the model path
251+ :param history: history of successful model paths
252+ :param printer: a model help printer
253+ """
254+ try :
255+ printer .print_model_help (model_path , ControlOptions .NORMAL )
256+
257+ # the print_model_help succeeded, add successful path to the history
258+ if history [- 1 ] != model_path :
259+ history .append (model_path )
260+
261+ except CLAException , ex :
262+ print ("" )
263+ print ("Error getting '" + model_path + "': " + ex .getLocalizedMessage ())
264+ print ("" )
265+ interactive_help_print_short_instructions ()
266+
267+ def interactive_help_print_short_instructions ():
268+ """
269+ Prints short instructions for interactive help.
270+ """
271+ print ("In interactive mode! Type 'help' for help." )
272+
273+
274+ def interactive_help_print_full_instructions ():
275+ """
276+ Prints full instructions for interactive help.
277+ """
278+ print ("" )
279+ print ("Commands:" )
280+ print ("" )
281+ print (" ls - list contents of current location" )
282+ print (" top, cd, cd /, cd top - go to \" top\" " )
283+ print (" cd x[/[...]] - relative change (go to child location x...)" )
284+ print (" cd section[:/[...]] - absolute change (go to exact section and location)" )
285+ print (" cd /folder[/...] - find section that contains the folder and go there" )
286+ print (" cd .. - go up" )
287+ print (" history - history of visited locations" )
288+ print (" exit - exit" )
289+ print ("" )
290+ print ("Sections:" )
291+ print ("" )
292+ print (" " + str (', ' .join (model .get_model_top_level_keys ())))
293+ print ("" )
294+ print ("Examples:" )
295+ print ("" )
296+ print (" cd topology" )
297+ print (" cd topology:/Server/Log/StdoutSeverity" )
298+ print (" cd /Server/Log/StdoutSeverity" )
299+ print ("" )
300+
301+
302+ def interactive_help_process_command (aliases , printer , model_path , command_str , history ):
303+ """
304+ Process an interactive help command.
305+ :param aliases: aliases
306+ :param printer: a model help printer
307+ :param model_path: current model path before applying command
308+ :param history: current history, a new model path added is added if command changes it
309+ :param command_str: the command
310+ """
311+
312+ if command_str == 'help' :
313+ interactive_help_print_full_instructions ()
314+
315+ elif command_str == 'history' :
316+ for line in history :
317+ print ("" )
318+ print (line )
319+
320+ elif command_str .count (' ' ) > 1 :
321+ print ("" )
322+ print ("Syntax error '" + command_str + "'" )
323+ print ("" )
324+ interactive_help_print_short_instructions ()
325+
326+ elif command_str == 'ls' or command_str == 'cd' or command_str == 'top' or command_str .startswith ('cd ' ):
327+ canonical_path = parse_dir_command_all (aliases , model_path , command_str )
328+ model_path = model_path_from_canonical_path (canonical_path )
329+ interactive_help_print_path (printer , model_path , history )
330+
331+ elif command_str :
332+ print ("" )
333+ print ("Unknown command '" + command_str + "'" )
334+ print ("" )
335+ interactive_help_print_short_instructions ()
336+
337+
338+ def interactive_help_main_loop (aliases , model_path , printer ):
339+ """
340+ Runs the interactive help.
341+ :param aliases: aliases
342+ :param model_path: the model path to start with
343+ :param printer: a model help printer
344+ :return: returns when user types 'exit'
345+ """
346+ _method_name = 'interactive_help_main_loop'
347+
348+ __logger .entering (model_path , class_name = _class_name , method_name = _method_name )
349+
350+ # setup starting history
351+ history = ['top' ]
352+
353+ # optionally get input from file instead of stdin (undocumented feature)
354+ input_file_name = os .environ .get ('WDT_INTERACTIVE_MODE_INPUT_FILE' )
355+ input_file = None
356+ if input_file_name :
357+ input_file = open (input_file_name , "r" )
358+
359+ # initial command (seeded from the command line)
360+ command_str = "cd " + model_path
361+
362+ print ("" )
363+ interactive_help_print_short_instructions ()
364+ print ("" )
365+ print ("Starting with '" + command_str + "'." )
366+
367+ while True :
368+ if command_str == 'exit' :
369+ break
370+
371+ # the "process command" prints the help (or error) for the command_str
372+ # plus appends a new path to the history if the str specifies a successful directory change
373+
374+ interactive_help_process_command (aliases , printer , history [- 1 ], command_str , history )
375+ print ("" )
376+
377+ # get the next command (from either stdin, or the input_file if input_file is set)
378+
379+ command_str = interactive_help_prompt (history [- 1 ], input_file )
380+
381+ __logger .exiting (class_name = _class_name , method_name = _method_name )
382+
383+
73384def print_help (model_path , model_context ):
74385 """
75386 Prints the folders and/or attributes for the specified given model_path,
@@ -95,7 +406,11 @@ def print_help(model_path, model_context):
95406
96407 aliases = Aliases (model_context )
97408 printer = ModelHelpPrinter (aliases , __logger )
98- printer .print_model_help (model_path , control_option )
409+
410+ if model_context .get_interactive_mode_option ():
411+ interactive_help_main_loop (aliases , model_path , printer )
412+ else :
413+ printer .print_model_help (model_path , control_option )
99414
100415 __logger .exiting (class_name = _class_name , method_name = _method_name )
101416 return ExitCode .OK
0 commit comments