/* * Beanfabrics Framework Copyright (C) by Michael Karneim, beanfabrics.org * Use is subject to license terms. See license.txt. */ package org.beanfabrics.swing.customizer.path; import java.net.URL; import java.util.ResourceBundle; import org.beanfabrics.Path; import org.beanfabrics.meta.PathNode; import org.beanfabrics.meta.TypeInfo; import org.beanfabrics.model.AbstractPM; import org.beanfabrics.model.IconTextPM; import org.beanfabrics.model.MapPM; import org.beanfabrics.model.OperationPM; import org.beanfabrics.model.PMManager; import org.beanfabrics.model.TextPM; import org.beanfabrics.support.OnChange; import org.beanfabrics.support.Operation; import org.beanfabrics.support.SortOrder; import org.beanfabrics.support.Validation; import org.beanfabrics.util.ResourceBundleFactory; import org.beanfabrics.validation.ValidationState; /** * The {@link PathBrowserPM} is the presentation model of the {@link PathBrowserPanel}. * * @author Michael Karneim */ public class PathBrowserPM extends AbstractPM { protected static final String KEY_MESSAGE_INVALID_PATH = "message.invalidPath"; private final ResourceBundle resourceBundle = ResourceBundleFactory.getBundle(PathBrowserPM.class); private static final URL SUCCESS_ICON = PathBrowserPM.class.getResource("agt_action_success.png"); private static final URL FAIL_ICON = PathBrowserPM.class.getResource("agt_action_fail.png"); private static final URL WARNING_ICON = PathBrowserPM.class.getResource("messagebox_warning.png"); TextPM currentSelectedPath = new TextPM(); TextPM currentSelectedType = new TextPM(); MapPM<PathNode, PathNodePM> children = new MapPM<PathNode, PathNodePM>(); OperationPM gotoSelectedChild = new OperationPM(); OperationPM gotoCurrentPath = new OperationPM(); OperationPM gotoParent = new OperationPM(); IconTextPM status = new IconTextPM(); private PathNode rootElement; private PathNode currentElement; private TypeInfo requiredModelTypeInfo; public PathBrowserPM() { PMManager.setup(this); currentSelectedType.setEditable(false); status.setEditable(false); updateStatus(); } public void setPathContext(PathContext pathContext) { if (pathContext == null) { throw new IllegalArgumentException("pathContext==null"); } rootElement = pathContext.root; requiredModelTypeInfo = pathContext.requiredModelTypeInfo; } public void setCurrentPath(Path path) { currentSelectedPath.setText(Path.getPathString(path)); PathNode pathElementInfo = rootElement == null ? null : rootElement.getNode(path); if (pathElementInfo == null) { loadChildren(null); } else { loadChildren(pathElementInfo.getParent()); selectChild(pathElementInfo); } } public Path getCurrentPath() { if (currentSelectedPath.isEmpty()) { return null; } else { Path path = Path.parse(currentSelectedPath.getText()); return path; } } private void loadChildren(PathNode aElement) { currentElement = aElement; Path currentPath = getCurrentPath(); children.clear(); if (aElement == null) { gotoParent.setDescription(""); // no owner -> so we load the root object PathNodePM rootCell = new PathNodePM(); rootCell.setData(rootElement); children.put(rootElement, rootCell); if (rootElement.getPath().equals(currentPath)) { children.getSelectedKeys().add(rootElement); } } else { gotoParent.setDescription("Up to "+currentElement.getTypeInfo().getJavaType().getSimpleName()); for (PathNode child : aElement.getChildren()) { PathNodePM cell = new PathNodePM(); cell.setData(child); children.put(child, cell); if (child.getPath().equals(currentPath)) { children.getSelectedKeys().add(child); } } } } public TypeInfo getCurrentModelTypeInfo() { Path path = getCurrentPath(); if (path != null && rootElement != null) { PathNode element = rootElement.getNode(path); if (element != null) { return element.getTypeInfo(); } } return null; } @OnChange(path = "children") void updateCurrentPath() { if (children.getSelection().isEmpty() == false) { PathNodePM first = children.getSelection().getFirst(); if (first != null) { String pathStr = Path.getPathString(first.getData().getPath()); currentSelectedPath.setText(pathStr); } } } @OnChange(path = "currentSelectedPath") void updateStatus() { if (currentSelectedPath.isEmpty()) { String txt = "Enter a valid path here."; if (requiredModelTypeInfo != null) { txt += " A path is valid if it points to an PresentationModel of type " + requiredModelTypeInfo.getName(); } status.setText("<html>" + txt + "</html>"); status.setIconUrl(WARNING_ICON); } else { ValidationState vState = currentSelectedPath.getValidationState(); status.setText(vState == null ? null : "<html>" + vState.getMessage() + "</html>"); status.setIconUrl(vState == null ? SUCCESS_ICON : FAIL_ICON); } } @OnChange(path = "currentSelectedPath") void updateNodeType() { TypeInfo desc = getCurrentModelTypeInfo(); if (desc == null) { currentSelectedType.setText(null); currentSelectedType.setDescription("Unknown type"); } else { String classname = desc.getName(); currentSelectedType.setText(getBasename(classname)); currentSelectedType.setDescription(classname); } } @Validation(path = "currentSelectedPath", message = "This path can not be resolved") @SortOrder(1) boolean canPathBeResolved() { Path path = Path.parse(currentSelectedPath.getText()); return (rootElement != null && rootElement.getNode(path) != null); } @Validation(path = "currentSelectedPath") @SortOrder(2) ValidationState matchesRequiredModelTypeInfo() { if (requiredModelTypeInfo != null) { TypeInfo currentModelTypeInfo = getCurrentModelTypeInfo(); boolean valid = (currentModelTypeInfo != null && requiredModelTypeInfo .isAssignableFrom(currentModelTypeInfo)); if (!valid) { String message = String.format(resourceBundle.getString(KEY_MESSAGE_INVALID_PATH), requiredModelTypeInfo.getName()); return new ValidationState(message); } } return null; } @Validation public boolean isPathValid() { return currentSelectedPath.isValid(); } @Validation(path = "gotoSelectedChild", message = "the selected model has no children") public boolean canGotoSelectedChild() { PathNodePM first = children.getSelection().getFirst(); if (first != null) { PathNode nextPathNode = first.getData(); if (nextPathNode.hasChildren() == true) { return true; } } return false; } @Operation public void gotoSelectedChild() { PathNodePM cell = children.getSelection().getFirst(); if (cell != null) { PathNode nextElement = cell.getData(); if (nextElement.hasChildren() == true) { loadChildren(nextElement); } } } @Validation(path = "gotoParent", message = "Already on top") public boolean canGotoParent() { return currentElement != null; } @Operation public void gotoParent() { if (currentElement == null) { return; } PathNode nextSelectedChild = currentElement; PathNode nextElement = currentElement.getParent(); loadChildren(nextElement); selectChild(nextSelectedChild); } private void selectChild(PathNode child) { if (children.containsKey(child)) { children.getSelectedKeys().clear(); children.getSelectedKeys().add(child); } } @Operation public void gotoCurrentPath() { if (rootElement != null) { Path pathToChild = getCurrentPath(); PathNode childPathInfo = rootElement.getNode(pathToChild); if (childPathInfo != null) { setCurrentPath(childPathInfo.getPath()); } else { // TODO (mk) we could provide some auto-completion here... } } // else ignore } private static String getBasename(String classname) { int idx = classname.lastIndexOf('$'); if (idx == -1) { idx = classname.lastIndexOf('.'); } if (idx == -1) { return classname; } else { return classname.substring(idx + 1); } } }