Skip to content

Commit 13c9818

Browse files
author
Tom Barnes
authored
Issue 979 - enhanced help (add bean descriptions, att descriptions, and interactive mode) (#1148)
1 parent 35210af commit 13c9818

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+1448
-28
lines changed

core/src/main/java/oracle/weblogic/deploy/util/WLSBeanHelp.java

Lines changed: 502 additions & 0 deletions
Large diffs are not rendered by default.

core/src/main/python/model_help.py

Lines changed: 317 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,14 @@
1414
# Jython tools don't require sys.path modification
1515

1616
from wlsdeploy.aliases.aliases import Aliases
17+
from wlsdeploy.aliases.wlst_modes import WlstModes
1718
from wlsdeploy.exception import exception_helper
1819
from wlsdeploy.logging.platform_logger import PlatformLogger
1920
from wlsdeploy.tool.modelhelp.model_help_printer import ModelHelpPrinter
2021
from wlsdeploy.tool.modelhelp.model_help_utils import ControlOptions
2122
from wlsdeploy.tool.util import model_context_helper
2223
from wlsdeploy.util import cla_helper
24+
from wlsdeploy.util import model
2325
from wlsdeploy.util.cla_utils import CommandLineArgUtil
2426
from wlsdeploy.util.exit_code import ExitCode
2527

@@ -34,7 +36,8 @@
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+
73384
def 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

core/src/main/python/wlsdeploy/aliases/alias_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
MERGE = 'merge'
2222
MODEL_NAME = 'model_name'
2323
NAME_VALUE = 'name_value'
24+
ONLINE_BEAN = 'online_bean'
2425
PASSWORD_TOKEN = "--FIX ME--"
2526
PATH_TOKEN = 'path_token'
2627
PREFERRED_MODEL_TYPE = 'preferred_model_type'

0 commit comments

Comments
 (0)