Skip to content

WindowBuilder Lifecycle

Patrick Ziegler edited this page Oct 9, 2025 · 1 revision

org.eclipse.wb.core.editor.multi.MultiPageEditor contains two pages:

  • Source - standard org.eclipse.jdt.internal.ui.javaeditor.CompilationUnitEditor
  • Design - org.eclipse.wb.core.editor.DesignPage

org.eclipse.wb.core.editor.DesignPage is a PageBook with two pages:

  • The actual GUI editor - org.eclipse.wb.core.editor.DesignComposite
  • Error display - org.eclipse.wb.core.editor.errors.ExceptionComposite

When switching from Source to Design in MultiPageEditor, DesignPage.handleActivate() is called, which triggers org.eclipse.wb.core.editor.UndoManager.activate(). If UndoManager detects that the ICompilationUnit source hasn't been parsed yet or has changed, it sets various listeners, and then triggers DesignPage.internal_refreshGEF().

DesignPage.internal_refreshGEF() performs two operations:

  • calls DesignPage.disposeAll(), which destroys the current model, including all resources it holds, and clears the GEF and palette.
  • raises IRunnableWithProgress, which calls DesignPage.internal_refreshGEF_withProgress(), handling errors and, if necessary, calling DesignPage.showExceptionOnDesignPane().

DesignPage.internal_refreshGEF_withProgress() uses JavaInfoParser.parse(m_compilationUnit), obtaining the root JavaInfo. It then configures this root JavaInfo with the DesignPageSite, which can be used to communicate with the current editor. Why does m_rootObject.refresh() trigger? Finally, the call to DesignComposite.refresh(m_rootObject, monitor) displays the GEF, component tree, palette, and actions.

org.eclipse.wb.core.parser.JavaInfoParser creates an ASTEditor, i.e., an AST CompilationUnit, and asks each org.eclipse.wb.core.parser.IParseFactory.getRootContext() method in turn. If a CompilationUnit with types related to this IParseFactory and its native toolkit is passed, it returns ParseRootContext.

ParseRootContext is a container with a root JavaInfo (only if we have something like "public class MyFrame extends JFrame"; if "main()" is used, it will be "null"); plus an ExecutionFlowDescription.

ExecutionFlowDescription is a description of how to traverse the AST. It starts with some MethodDeclarations, and then org.eclipse.wb.core.eval.ExecutionFlowUtils walks the AST Statement by Statement, making transitions to the local methods being called, using org.eclipse.wb.core.model.creation.ThisCreationSupport and CGLib, even if the calls to these local methods are made from the bytecode of a subperclass.

If none of the IParseFactories return a ParseRootContext for JavaInfoParser, then it's assumed we must have a "main()" function. If there is no "main()", an error will occur.

When JavaInfoParser receives the ParseRootContext, it asks ExecutionFlowUtils to traverse the AST for this execution flow using the visitor org.eclipse.wb.core.parser.JavaInfoParser.ExecutionFlowParseVisitor. During this process, each encountered ClassInstanceCreation or MethodInvocation is passed to each IParseFactory. If the IParseFactory determines that the passed-in component is a creation from its toolkit, it creates it (with the appropriate CreationSupport) and returns it. The first one to succeed wins, but there shouldn't be any conflicts.

ExecutionFlowParseVisitor.bindChild_MethodInvocation() or bindChild_ClassInstanceCreation() is used to create declaration-based parent/child relationships based on parent/child flags in *.wbp-component.xml declarations.

After all ASTs have been traversed, JavaInfoParser.getRoot() is called. It:

  • sends m_editorState.getBroadcast().getListener(JavaEventListener.class).bindComponents(m_components), giving it a chance to bind anything that would like to bind, given all the created JavaInfo objects.
  • calls JavaInfoUtils.bindBinaryComponents(m_components), which binds the toolkit object models in parent/child if the toolkit objects are actually bound, but this happened in code we haven't seen—say, somewhere in the bytecode of the parent class. - the root JavaInfo is selected as the JavaInfo with the longest member... that is, the one with the largest number of children... although it turns out it started correctly...
  • this root JavaInfo is notified to everyone that it is the main one: root.getBroadcastJava().treeAlmostComplete(root, m_components)

Parsing is complete at this point.

During editing, all edit operations are performed between calls to ObjectInfo.start/endEdit(), either directly or indirectly via org.eclipse.wb.core.utils.execution.ExecutionUtils.run(ObjectInfo objectInfo, RunnableEx runnable). When ObjectInfo.endEdit() notices that we've completed the top-level operation, it calls ObjectInfo.refresh(). After that, it calls various ObjectInfo.refresh*() methods. Specifically, JavaInfo.refresh_create(), which performs the same AST traversal for execution flow, as JavaInfoParser. This time, models aren't created; they're simply executed via org.eclipse.wb.core.model.JavaInfoEvaluationHelper. Finally, ObjectInfo.refresh() sends two notifications:

  • getBroadcastObject().refreshed();
  • getBroadcastObject().refreshed2(); GEF listens to one or both notifications and performs a refresh() for the EditParts.

Almost all GEF commands are implemented via org.eclipse.wb.core.gef.command.EditCommand, which uses ExecutionUtils.run(ObjectInfo objectInfo, RunnableEx runnable), thus initiating a refresh() operation upon completion.

Clone this wiki locally