| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.netbeans.modules.web.jsf.navigation; |
| |
| import java.awt.Cursor; |
| import java.awt.Dialog; |
| import java.awt.EventQueue; |
| import java.beans.PropertyChangeEvent; |
| import java.beans.PropertyChangeListener; |
| import java.io.IOException; |
| import java.lang.String; |
| import java.lang.ref.WeakReference; |
| import javax.swing.SwingUtilities; |
| import org.netbeans.modules.web.jsf.api.ConfigurationUtils; |
| import org.netbeans.modules.web.jsf.api.editor.JSFConfigEditorContext; |
| import org.netbeans.modules.web.jsf.api.facesmodel.FacesConfig; |
| import org.netbeans.modules.web.jsf.api.facesmodel.JSFConfigModel; |
| import org.netbeans.modules.web.jsf.api.facesmodel.NavigationCase; |
| import org.netbeans.modules.web.jsf.api.facesmodel.NavigationRule; |
| import org.openide.filesystems.FileObject; |
| import org.openide.filesystems.FileStateInvalidException; |
| import org.openide.loaders.DataObject; |
| import org.openide.loaders.DataObjectNotFoundException; |
| import org.openide.nodes.AbstractNode; |
| import org.openide.nodes.Children; |
| import org.openide.nodes.Node; |
| import org.openide.util.Exceptions; |
| import org.openide.filesystems.FileChangeListener; |
| import java.util.*; |
| import java.util.Map.Entry; |
| import java.util.concurrent.atomic.AtomicBoolean; |
| import java.util.logging.Level; |
| import java.util.logging.LogRecord; |
| import java.util.logging.Logger; |
| import java.util.prefs.Preferences; |
| import javax.swing.JButton; |
| import javax.swing.JEditorPane; |
| import org.netbeans.api.progress.ProgressUtils; |
| import org.netbeans.modules.web.api.webmodule.WebModule; |
| import org.netbeans.modules.web.jsf.api.facesmodel.JSFConfigComponent; |
| import org.openide.DialogDescriptor; |
| import org.openide.DialogDisplayer; |
| import org.openide.util.NbBundle; |
| import org.netbeans.modules.web.jsf.navigation.PageFlowToolbarUtilities.Scope; |
| import org.netbeans.modules.web.jsf.navigation.pagecontentmodel.PageContentModelProvider; |
| import org.openide.NotifyDescriptor; |
| import org.openide.awt.StatusDisplayer; |
| import org.openide.cookies.EditorCookie; |
| import org.openide.util.NbPreferences; |
| import org.openide.cookies.EditCookie; |
| import org.openide.filesystems.FileSystem; |
| import org.openide.util.Lookup; |
| |
| /** |
| * |
| * @author joelle lam |
| */ |
| public class PageFlowController { |
| |
| private PageFlowView view; |
| private JSFConfigModel configModel; |
| private DataObject configDataObj; |
| private final Map<NavigationCase, NavigationCaseEdge> navCase2NavCaseEdge = new WeakHashMap<NavigationCase, NavigationCaseEdge>(); |
| private final Map<NavigationRule, String> navRule2String = new WeakHashMap<NavigationRule, String>(); |
| private final HashMap<String, WeakReference<Page>> pageName2Page = new HashMap<String, WeakReference<Page>>(); //Should this be synchronized. |
| // public static final String DEFAULT_DOC_BASE_FOLDER = "web"; //NOI18NF |
| private static final String NO_WEB_FOLDER_WARNING = NbBundle.getMessage(PageFlowController.class, "MSG_NoWebFolder"); |
| private static final String NO_WEB_FOLDER_TITLE = NbBundle.getMessage(PageFlowController.class, "TLE_NoWebFolder"); |
| private volatile FileObject webFolder; |
| private AtomicBoolean isListenerRegistered = new AtomicBoolean(false); |
| |
| /** Creates a new instance of PageFlowController |
| * @param context |
| * @param view |
| */ |
| public PageFlowController(JSFConfigEditorContext context, PageFlowView view) { |
| this.view = view; |
| FileObject configFile = context.getFacesConfigFile(); |
| |
| try { |
| configDataObj = DataObject.find(configFile); |
| } catch (DataObjectNotFoundException donfe) { |
| Exceptions.printStackTrace(donfe); |
| } |
| configModel = ConfigurationUtils.getConfigModel(configFile, true); |
| |
| assert configModel != null; |
| // Project project = FileOwnerQuery.getOwner(configFile); |
| // webFolder = project.getProjectDirectory().getFileObject(DEFAULT_DOC_BASE_FOLDER); |
| webFolder = PageFlowView.getWebFolder(configFile); |
| webFiles = setupWebFiles(webFolder); |
| } |
| private Collection<FileObject> webFiles; |
| |
| private Collection<FileObject> setupWebFiles(FileObject webFolder) { |
| final Collection<FileObject> myWebFiles = new LinkedList<FileObject>(); |
| if (webFolder == null) { |
| ifNecessaryShowNoWebFolderDialog(); |
| } else { |
| // loading all the relevant files may take quite a while - see #177459 |
| // they're also loaded again every time the page flow editor is opened; |
| // would be better to hold onto them and listen for changes in the web dir(s). |
| // for now fixing it this way as it is safer (i'm not familiar |
| // with the page flow editor, and this is probably not the best place for this code. |
| // but in any case this should be a pretty safe fix). |
| AtomicBoolean canceled = new AtomicBoolean(); |
| ProgressUtils.runOffEventDispatchThread(new Runnable() { |
| |
| @Override |
| public void run() { |
| myWebFiles.addAll(getAllProjectRelevantFilesObjects()); |
| } |
| }, NbBundle.getMessage(PageFlowController.class, "MSG_LoadingWebFiles"), canceled, false); |
| } |
| return myWebFiles; |
| } |
| |
| protected void ifNecessaryShowNoWebFolderDialog() { |
| if (isShowNoWebFolderDialog()) { |
| |
| final NotWebFolder panel = new NotWebFolder(NO_WEB_FOLDER_WARNING); |
| DialogDescriptor descriptor = new DialogDescriptor(panel, NO_WEB_FOLDER_TITLE, true, NotifyDescriptor.PLAIN_MESSAGE, NotifyDescriptor.YES_OPTION, null); |
| JButton okButton = new JButton( |
| NbBundle.getMessage(PageFlowController.class, "MSG_OkButtonText")); //NOI18N |
| descriptor.setOptions(new Object[]{okButton}); |
| descriptor.setMessageType(NotifyDescriptor.PLAIN_MESSAGE); |
| descriptor.setClosingOptions(new Object[]{okButton}); |
| descriptor.setOptionsAlign(DialogDescriptor.BOTTOM_ALIGN); |
| final Dialog d = DialogDisplayer.getDefault().createDialog(descriptor); |
| d.setSize(400, 200); |
| d.setVisible(true); |
| |
| setShowNoWebFolderDialog(panel.getShowDialog()); |
| } |
| } |
| |
| public void destroy() { |
| webFolder = null; |
| configModel = null; |
| view = null; |
| webFiles.clear(); |
| navCase2NavCaseEdge.clear(); |
| navRule2String.clear(); |
| pageName2Page.clear(); |
| |
| } |
| private static final String PROP_SHOW_NO_WEB_FOLDER = "showNoWebFolder"; // NOI18N |
| |
| public final void setShowNoWebFolderDialog(boolean show) { |
| getPreferences().putBoolean(PROP_SHOW_NO_WEB_FOLDER, show); |
| } |
| |
| private static Preferences getPreferences() { |
| return NbPreferences.forModule(PageFlowController.class); |
| } |
| |
| public final boolean isShowNoWebFolderDialog() { |
| return getPreferences().getBoolean(PROP_SHOW_NO_WEB_FOLDER, true); |
| } |
| private PropertyChangeListener pcl; |
| private FileChangeListener fcl; |
| |
| public synchronized boolean isListenerRegistered() { |
| return isListenerRegistered.get(); |
| } |
| |
| // private ComponentListener cl; |
| public synchronized void registerListeners() { |
| if (isListenerRegistered.get()) { |
| return; |
| } |
| |
| if (pcl == null && configModel != null) { |
| pcl = new FacesModelPropertyChangeListener(this); |
| configModel.addPropertyChangeListener(pcl); |
| isListenerRegistered.set(true); |
| } else { |
| return; |
| } |
| |
| FileObject myWebFolder = getWebFolder(); |
| if (fcl == null) { |
| fcl = new WebFolderListener(this); |
| if (myWebFolder != null) { |
| try { |
| FileSystem fileSystem = myWebFolder.getFileSystem(); |
| fileSystem.addFileChangeListener(fcl); |
| |
| } catch (FileStateInvalidException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Unregister any listeners. |
| */ |
| public void unregisterListeners() { |
| |
| if (pcl != null) { |
| if (configModel != null) { |
| configModel.removePropertyChangeListener(pcl); |
| } |
| pcl = null; |
| } |
| |
| FileObject myWebFolder = getWebFolder(); |
| if (fcl != null && myWebFolder != null) { |
| try { |
| FileSystem fileSystem = myWebFolder.getFileSystem(); |
| fileSystem.removeFileChangeListener(fcl); |
| fcl = null; |
| } catch (FileStateInvalidException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| } |
| } |
| |
| FileChangeListener getFCL() { |
| return fcl; |
| } |
| |
| void flushGraphIfDirty() { |
| |
| if (isFilesDirty) { |
| webFiles = setupWebFiles(webFolder); |
| isFilesDirty = false; |
| } |
| if (isGraphDirty) { |
| if (isWellFormed) { |
| EventQueue.invokeLater(new Runnable() { |
| |
| public void run() { |
| if (view == null) { |
| // XXX #145074 It is destroyed already, revise that pattern. |
| return; |
| } |
| view.removeUserMalFormedFacesConfig(); // Does clear graph take care of this? |
| setupGraph(); |
| } |
| }); |
| } else { |
| EventQueue.invokeLater(new Runnable() { |
| |
| public void run() { |
| if (view == null) { |
| // XXX #145074 It is destroyed already, revise that pattern. |
| return; |
| } |
| view.clearGraph(); |
| view.warnUserMalFormedFacesConfig(); |
| } |
| }); |
| |
| } |
| isGraphDirty = false; |
| } |
| } |
| private boolean isWellFormed = true; |
| private boolean isGraphDirty = false; |
| private boolean isFilesDirty = false; |
| |
| protected void setGraphDirtyWellFormed(boolean isWellFormed) { |
| isGraphDirty = true; |
| this.isWellFormed = isWellFormed; |
| } |
| |
| protected void setGraphDirty() { |
| isGraphDirty = true; |
| } |
| |
| protected void setFilesDirty() { |
| this.isFilesDirty = true; |
| isGraphDirty = true; |
| } |
| |
| public boolean isCurrentScope(Scope scope) { |
| return PageFlowToolbarUtilities.getInstance(view).getCurrentScope().equals(scope); |
| } |
| |
| /** |
| * Creates a Link in the FacesConfiguration |
| * @param source from page, if null an NPE will be thrown. |
| * @param target to page, if null an NPE will be thrown. |
| * @param pinNode if null then it was not conntect to a pin. |
| * @return |
| */ |
| public NavigationCase createLink(Page source, Page target, Pin pinNode) { |
| if (source == null) { |
| throw new NullPointerException("Source page should not be null."); |
| } else if (target == null) { |
| throw new NullPointerException("Target page should not be null"); |
| } |
| |
| String sourceName = source.getDisplayName(); |
| int caseNum = 1; |
| |
| configModel.startTransaction(); |
| FacesConfig facesConfig = configModel.getRootComponent(); |
| NavigationRule navRule = getRuleWithFromViewID(facesConfig, source.getDisplayName()); |
| NavigationCase navCase = configModel.getFactory().createNavigationCase(); |
| if (navRule == null) { |
| navRule = configModel.getFactory().createNavigationRule(); |
| FacesModelUtility.setFromViewId(navRule, source.getDisplayName()); |
| facesConfig.addNavigationRule(navRule); |
| navRule2String.put(navRule, FacesModelUtility.getFromViewIdFiltered(navRule)); |
| } else { |
| caseNum = getNewCaseNumber(navRule); |
| } |
| String caseName = CASE_STRING + Integer.toString(caseNum); |
| |
| if (pinNode != null) { |
| pinNode.setFromOutcome(caseName); |
| } |
| navCase.setFromOutcome(caseName); |
| |
| FacesModelUtility.setToViewId(navCase, target.getDisplayName()); |
| navRule.addNavigationCase(navCase); |
| |
| try { |
| configModel.endTransaction(); |
| configModel.sync(); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } catch (IllegalStateException ise) { |
| Exceptions.printStackTrace(ise); |
| } |
| |
| return navCase; |
| } |
| |
| public void updatePageItems(Page pageNode) { |
| view.resetNodeWidget(pageNode, true); |
| view.validateGraph(); |
| } |
| private static final String CASE_STRING = "case"; |
| |
| private int getNewCaseNumber(NavigationRule navRule) { |
| Collection<String> caseOutcomes = new HashSet<String>(); |
| List<NavigationCase> navCases = navRule.getNavigationCases(); |
| for (NavigationCase navCase : navCases) { |
| caseOutcomes.add(navCase.getFromOutcome()); |
| // caseOutcomes.add(navCase.getFromAction()); |
| } |
| |
| int caseNum = 1; |
| while (true) { |
| if (!caseOutcomes.contains(CASE_STRING + Integer.toString(caseNum))) { |
| return caseNum; |
| } |
| caseNum++; |
| } |
| } |
| |
| /** |
| * @return the navigation rule. This will be null if none was found |
| **/ |
| private NavigationRule getRuleWithFromViewID(FacesConfig facesConfig, String fromViewId) { |
| |
| for (NavigationRule navRule : facesConfig.getNavigationRules()) { |
| String rulefromViewId = FacesModelUtility.getFromViewIdFiltered(navRule); |
| if (rulefromViewId != null && rulefromViewId.equals(fromViewId)) { |
| // Match Found |
| return navRule; |
| } |
| } |
| |
| return null; |
| } |
| |
| private final Collection<FileObject> getAllProjectRelevantFilesObjects() { |
| return getProjectKnownFileOjbects(getWebFolder()); |
| } |
| |
| private Collection<FileObject> getProjectKnownFileOjbects(FileObject folder) { |
| Collection<FileObject> projectKnownFiles = new LinkedList<FileObject>(); |
| |
| FileObject[] childrenFiles = new FileObject[]{}; |
| if (folder != null) { |
| childrenFiles = folder.getChildren(); |
| } |
| for (FileObject file : childrenFiles) { |
| if (!file.isFolder()) { |
| if (isKnownFile(file)) { |
| projectKnownFiles.add(file); |
| } |
| } else if (isKnownFolder(file)) { |
| projectKnownFiles.addAll(getProjectKnownFileOjbects(file)); |
| } |
| } |
| |
| return projectKnownFiles; |
| } |
| |
| /** |
| * Check if the file type in known. |
| * @param file the fileobject type to check. If null, throws NPE. |
| * @return if it is of type jsp, jspf, or html it will return true. |
| */ |
| public final boolean isKnownFile(FileObject file) { |
| String[] knownMimeTypes = {"text/x-jsp", "text/html", "text/xhtml"}; //NOI18N |
| String mimeType = file.getMIMEType(knownMimeTypes); |
| if (mimeType.equals("text/x-jsp") && !file.getExt().equals("jspf")) { //NOI18N |
| return true; |
| } else if (mimeType.equals("text/html") || mimeType.equals("text/xhtml")) { //NOI18N |
| return true; |
| } |
| return false; |
| } |
| |
| public final boolean isKnownFolder(FileObject folder) { |
| /* If it is not a folder return false*/ |
| if (!folder.isFolder()) { |
| return false; |
| } |
| /* If it does not exist within WebFolder return false */ |
| if (!folder.getPath().contains(getWebFolder().getPath())) { |
| return false; |
| } |
| /* If it exists withing WEB-INF or META-INF return false */ |
| if (folder.getPath().contains("WEB-INF") || folder.getPath().contains("META-INF")) { |
| return false; |
| } |
| return true; |
| } |
| |
| /** |
| * Setup The Graph |
| * Should only be called by init(); |
| * |
| **/ |
| public boolean setupGraph() { |
| view.saveLocations(); |
| return setupGraphNoSaveData(); |
| } |
| private PropertyChangeListener otherFacesConfigListener = null; |
| |
| private PropertyChangeListener getOtherFacesConfigListener() { |
| if (otherFacesConfigListener == null) { |
| return new OtherFacesModelListener(); |
| } |
| return otherFacesConfigListener; |
| } |
| |
| private void removeOtherFacesConfigListener() { |
| WebModule webModule = WebModule.getWebModule(getWebFolder()); |
| FileObject[] configFiles = ConfigurationUtils.getFacesConfigFiles(webModule); |
| for (FileObject aConfigFile : configFiles) { |
| JSFConfigModel aConfigModel = ConfigurationUtils.getConfigModel(aConfigFile, true); |
| aConfigModel.removePropertyChangeListener(otherFacesConfigListener); |
| } |
| otherFacesConfigListener = null; |
| } |
| |
| protected void releaseGraphInfo() { |
| /* This listener is only created when it was a All_FACES scope */ |
| if (otherFacesConfigListener != null) { |
| removeOtherFacesConfigListener(); |
| } |
| |
| view.clearGraph(); |
| clearPageName2Page(); |
| navCase2NavCaseEdge.clear(); |
| navRule2String.clear(); |
| |
| } |
| |
| public boolean setupGraphNoSaveData() { |
| LOGGER.entering(PageFlowController.class.toString(), "setupGraphNoSaveData()"); |
| |
| assert configModel != null; |
| // assert webFolder != null; |
| assert webFiles != null; |
| releaseGraphInfo(); |
| |
| FacesConfig facesConfig = configModel.getRootComponent(); |
| |
| if (facesConfig == null) { |
| return false; |
| } |
| |
| /* If the most recently saved xml doc is malformed, we should know about it through this try statement. */ |
| try { |
| List<NavigationRule> rules = null; |
| if (isCurrentScope(Scope.SCOPE_FACESCONFIG)) { |
| rules = facesConfig.getNavigationRules(); |
| for (NavigationRule navRule : rules) { |
| navRule2String.put(navRule, FacesModelUtility.getFromViewIdFiltered(navRule)); |
| } |
| Collection<String> pagesInConfig = getFacesConfigPageNames(rules); |
| createFacesConfigPages(pagesInConfig); |
| } else if (isCurrentScope(Scope.SCOPE_PROJECT)) { |
| rules = facesConfig.getNavigationRules(); |
| for (NavigationRule navRule : rules) { |
| navRule2String.put(navRule, FacesModelUtility.getFromViewIdFiltered(navRule)); |
| } |
| Collection<String> pagesInConfig = getFacesConfigPageNames(rules); |
| createAllProjectPages(pagesInConfig); |
| } else if (isCurrentScope(Scope.SCOPE_ALL_FACESCONFIG)) { |
| List<NavigationRule> allRules = new ArrayList<NavigationRule>(); |
| FileObject myWebFolder = getWebFolder(); |
| if (myWebFolder != null) { |
| WebModule webModule = WebModule.getWebModule(myWebFolder); |
| FileObject[] configFiles = ConfigurationUtils.getFacesConfigFiles(webModule); |
| for (FileObject aConfigFile : configFiles) { |
| JSFConfigModel aConfigModel = ConfigurationUtils.getConfigModel(aConfigFile, true); |
| if (aConfigModel != null) { |
| allRules.addAll(aConfigModel.getRootComponent().getNavigationRules()); |
| if (!configModel.equals(aConfigModel)) { |
| aConfigModel.addPropertyChangeListener(getOtherFacesConfigListener()); |
| } |
| } |
| } |
| for (NavigationRule navRule : allRules) { |
| navRule2String.put(navRule, FacesModelUtility.getFromViewIdFiltered(navRule)); |
| } |
| Collection<String> pagesInConfig = getFacesConfigPageNames(allRules); |
| createFacesConfigPages(pagesInConfig); |
| rules = allRules; |
| } else { |
| /* If no web module exists don't worry about other faces-config files */ |
| rules = facesConfig.getNavigationRules(); |
| for (NavigationRule navRule : rules) { |
| navRule2String.put(navRule, FacesModelUtility.getFromViewIdFiltered(navRule)); |
| } |
| Collection<String> pagesInConfig = getFacesConfigPageNames(rules); |
| createAllProjectPages(pagesInConfig); |
| } |
| } |
| createAllEdges(rules); |
| view.validateGraph(); |
| LOGGER.log(new LogRecord(Level.FINE, "PageFlowEditor # Rules: " + rules.size() + "\n" + " # WebPages: " + webFiles.size() + "\n" + " # TotalPages: " + pageName2Page.size())); |
| } catch (IllegalStateException ise) { |
| view.warnUserMalFormedFacesConfig(); |
| view.validateGraph(); |
| LOGGER.log(new LogRecord(Level.FINE, "Illegal SateException thrown: " + ise.toString())); |
| } |
| LOGGER.exiting(PageFlowController.class.toString(), "setupGraphNoSaveData()"); |
| return true; |
| } |
| |
| private class OtherFacesModelListener implements PropertyChangeListener { |
| |
| public void propertyChange(PropertyChangeEvent evt) { |
| |
| EventQueue.invokeLater(new Runnable() { |
| |
| public void run() { |
| if (view == null) { |
| // XXX #145074 It is destroyed already, revise that pattern. |
| return; |
| } |
| setupGraph(); |
| } |
| }); |
| } |
| } |
| |
| private void createAllEdges(List<NavigationRule> rules) { |
| |
| List<NavigationRule> editableRules = configModel.getRootComponent().getNavigationRules(); |
| for (NavigationRule rule : rules) { |
| List<NavigationCase> navCases = rule.getNavigationCases(); |
| |
| /* this is for ALL_FACES_CONFIG scope*/ |
| boolean isModifableEdge = editableRules.contains(rule) ? true : false; |
| |
| for (NavigationCase navCase : navCases) { |
| |
| NavigationCaseEdge navEdge = new NavigationCaseEdge(this, navCase); |
| navCase2NavCaseEdge.put(navCase, navEdge); |
| navEdge.setModifiable(isModifableEdge); |
| if (navEdge.getFromViewId() != null && navEdge.getToViewId() != null) { |
| createEdge(navEdge); |
| } |
| } |
| } |
| } |
| |
| /** |
| * Creates and edge in the scene, this method does not add an reference in |
| * the faces configuration. In general it is best to call createLink |
| * as that will call createEdge indirectly through the faces model listener. |
| * @param caseNode a NavigationCaseEdge. If null, will throw NPE. |
| */ |
| protected void createEdge(NavigationCaseEdge caseNode) { |
| String fromPage = caseNode.getFromViewId(); |
| String toPage = caseNode.getToViewId(); |
| if (getPageName2Page(fromPage) == null || getPageName2Page(toPage) == null) { |
| System.err.println("Why is this node null? CaseNode: " + caseNode); |
| System.err.println("FromPage: " + fromPage); |
| System.err.println("ToPage: " + toPage); |
| Thread.dumpStack(); |
| } else { |
| view.createEdge(caseNode, getPageName2Page(fromPage), getPageName2Page(toPage)); |
| } |
| } |
| |
| private Collection<String> getFacesConfigPageNames(Collection<NavigationRule> navRules) { |
| // Get all the pages in the faces config. But don't list them twice. |
| Collection<String> pages = new HashSet<String>(); |
| for (NavigationRule navRule : navRules) { |
| String pageName = FacesModelUtility.getFromViewIdFiltered(navRule); |
| pages.add(pageName); |
| Collection<NavigationCase> navCases = navRule.getNavigationCases(); |
| for (NavigationCase navCase : navCases) { |
| // String toPage = navCase.getToViewId(); |
| String toPage = FacesModelUtility.getToViewIdFiltered(navCase); |
| if (toPage != null) { |
| pages.add(toPage); |
| } |
| } |
| } |
| return pages; |
| } |
| public java.util.Stack<String> PageFlowCreationStack = new java.util.Stack<String>(); |
| private int PageFlowCreationCount = 0; |
| |
| /** |
| * Create a Page from a node |
| * This method |
| * does not actually add the pages to the scene. It just creates the |
| * component. You will need to call scene.createNode(page) if you want. |
| * @param node the node or dataobject node delegate of a given fileobject. |
| * Use dataObject.find(fileObject).getNodeDelegate for the given |
| * page. If no dataObject backing the page, call createPage(String) |
| * @return page the Page that was created. |
| */ |
| public Page createPage(Node node) { |
| Page pageNode = new Page(this, node); |
| Calendar rightNow = Calendar.getInstance(); |
| PageFlowCreationStack.push("\n" + PageFlowCreationCount + ". " + rightNow.get(Calendar.MINUTE) + ":" + rightNow.get(Calendar.SECOND) + " - " + pageNode); |
| PageFlowCreationCount++; |
| return pageNode; |
| } |
| |
| /* |
| * Create PageFlow from a string with no backing page. This method |
| * does not actually add the pages to the scene. It just creates the |
| * component. You will need to call scene.createNode(page) if you want |
| * to add it to the scene. |
| * @param name the string of the name of the page to create |
| * If null is passed, NPE thrown. |
| * If empty string assertion thrown and null returned. |
| * @return page the Page that was created. |
| */ |
| public Page createPage(String pageName) { |
| Page node = null; |
| if (pageName == null) { |
| throw new NullPointerException("Page name string is null"); |
| } |
| assert pageName.length() != 0; |
| Node tmpNode = new AbstractNode(Children.LEAF); |
| tmpNode.setName(pageName); |
| node = createPage(tmpNode); |
| return node; |
| } |
| public java.util.Stack<String> PageFlowDestroyStack = new java.util.Stack<String>(); |
| private int PageFlowDestroyCount = 0; |
| |
| /** |
| * Destroys the page in the scene (removing the page content model and |
| * the page content listeners). This odes not actual destroy the dataobject |
| * or the backing file object. |
| * @param page Page to be deleted. |
| */ |
| private void destroyPageFlowNode(Page page) { |
| if (page != null) { |
| page.destroy2(); |
| Calendar rightNow = Calendar.getInstance(); |
| PageFlowDestroyStack.push("\n" + PageFlowDestroyCount + ". " + rightNow.get(Calendar.MINUTE) + ":" + rightNow.get(Calendar.SECOND) + " - " + page); |
| PageFlowDestroyCount++; |
| } |
| } |
| |
| private void createAllProjectPages(Collection<String> pagesInConfig) { |
| |
| Collection<String> pages = new HashSet<String>(pagesInConfig); |
| |
| //Create all pages in the project... |
| FileObject[] webFilesTmp = webFiles.toArray(new FileObject[0]);//Use copy because you may need to remove these files. |
| for (FileObject webFile : webFilesTmp) { |
| //DISPLAYNAME: |
| String webFileName = Page.getFolderDisplayName(getWebFolder(), webFile); |
| Page node = null; |
| try { |
| node = createPage((DataObject.find(webFile)).getNodeDelegate()); |
| view.createNode(node, null, null); |
| //Do not remove the webFile page until it has been created with a data Node. If the dataNode throws and exception, then it can be created with an Abstract node. |
| pages.remove(webFileName); |
| } catch (DataObjectNotFoundException ex) { |
| webFiles.remove(webFile); //Remove this file because it may have been deleted. |
| } |
| } |
| |
| //Create any pages that don't actually exist but are defined specified by the config file. |
| for (String pageName : pages) { |
| if (pageName != null) { |
| Node tmpNode = new AbstractNode(Children.LEAF); |
| tmpNode.setName(pageName); |
| Page node = createPage(tmpNode); |
| view.createNode(node, null, null); |
| } |
| } |
| } |
| |
| /** |
| * Givena pageName, look through the list of predefined webFiles and return the matching fileObject |
| * @return FileObject for which the match was found or null of none was found. |
| **/ |
| private FileObject getFileObject(String pageName) { |
| for (FileObject webFile : webFiles) { |
| //DISPLAYNAME: |
| String webFileName = Page.getFolderDisplayName(getWebFolder(), webFile); |
| // String webFileName = webFile.getNameExt(); |
| if (webFileName.equals(pageName)) { |
| return webFile; |
| } |
| } |
| return null; |
| } |
| |
| private void createFacesConfigPages(Collection<String> pagesInConfig) { |
| Collection<String> pages = new HashSet<String>(pagesInConfig); |
| |
| for (String pageName : pages) { |
| if (pageName != null) { |
| FileObject file = getFileObject(pageName); |
| Node wrapNode = null; |
| if (file == null) { |
| wrapNode = new AbstractNode(Children.LEAF); |
| wrapNode.setName(pageName); |
| } else { |
| try { |
| wrapNode = (DataObject.find(file)).getNodeDelegate(); |
| } catch (DataObjectNotFoundException donfe) { |
| Exceptions.printStackTrace(donfe); |
| } |
| } |
| Page node = createPage(wrapNode); |
| view.createNode(node, null, null); |
| } |
| } |
| } |
| private static final Logger LOGGER = Logger.getLogger(PageFlowController.class.getName()); |
| |
| /** |
| * Remove the page from the hashtable of string (or pages names ) to actual |
| * pages. Use permDestroy value to destroy the page in the scene completely. |
| * @param page that you want to remove. |
| * @param permDestroy true - destroys the page in the scene (removing the |
| * page content model and the page content listeners). |
| * This does not actual destroy the dataobject |
| * or the backing file object. |
| * false - if you just want to remove it from the list |
| * with the associated name. |
| * @return page that was removed. |
| */ |
| public Page removePageName2Page(Page page, boolean permDestroy) { |
| return removePageName2Page(page.getDisplayName(), permDestroy); |
| } |
| |
| /** |
| * Refer to removePageName2Page(Page page, boolean permDestroy) for details |
| * @param pageName the string value of the page name that you want removed. |
| * @param destroy |
| * @return |
| */ |
| public Page removePageName2Page(String pageName, boolean permDestroy) { |
| LOGGER.finest("PageName2Page: remove " + pageName); |
| checkAWTThread(); |
| synchronized (pageName2Page) { |
| Page node = null; |
| WeakReference<Page> nodeRef = pageName2Page.remove(pageName); |
| if (nodeRef != null) { |
| node = nodeRef.get(); |
| if (permDestroy) { |
| destroyPageFlowNode(node); |
| } |
| } |
| return node; |
| } |
| } |
| |
| /** |
| * Replace page name in PageName2Node HasMap. This is general used in a |
| * page rename. In general this removes the old Page and add the new one with |
| * the given name. |
| * @param page Page that should be added into the map. If null, NPE thrown |
| * and nothing removed from the map. |
| * @param String newName String that you want to assign to the page. |
| * @param String oldName String that was assigned to the page. |
| * @return true if page was found to replace, false is page was not found. |
| **/ |
| public boolean replacePageName2Page(Page page, String newName, String oldName) { |
| |
| LOGGER.finest("PageName2Page: replace " + oldName + " to " + newName); |
| //assert (newName.length() > 0); |
| //assert (oldName.length() > 0); |
| |
| if (page == null) { |
| throw new NullPointerException("Page can not be null."); |
| } |
| |
| checkAWTThread(); |
| synchronized (pageName2Page) { |
| WeakReference<Page> page2Ref = pageName2Page.remove(oldName); |
| if (page2Ref != null) { |
| Page pageFound = page2Ref.get(); |
| |
| if (pageFound != null) { |
| LOGGER.finest("Trying to replace page in map, but page not found:" + page); |
| } |
| pageName2Page.put(newName, new WeakReference<Page>(page)); |
| return true; |
| } |
| return false; |
| } |
| } |
| |
| /** |
| * Clears the pageName 2 Page mapping. Generally you want do this when you |
| * are about to throw everything in the scene away. This keeps references |
| * from being kept. |
| */ |
| protected void clearPageName2Page() { |
| LOGGER.finest("PageName2Page: clear"); |
| Set<String> keys; |
| synchronized (pageName2Page) { |
| keys = new HashSet<String>(pageName2Page.keySet()); |
| } |
| for (String key : keys) { |
| removePageName2Page(key, true); |
| } |
| } |
| |
| /** |
| * Associate a page with a given string name for future reference. In general |
| * this method is called by a Page object to add itself. Really no other classes |
| * should use this method. |
| * @param displayName name of the page you would like to reference it with (key) |
| * displayName can not be an empty string. |
| * @param page Page to be associated with the string. If null, NPE thrown. |
| */ |
| protected void putPageName2Page(String displayName, Page page) { |
| |
| LOGGER.finest("PageName2Page: put " + displayName); |
| //assert displayName.length() != 0; |
| if (page == null) { |
| throw new NullPointerException("putPageName2Page does not accept null pages."); |
| } |
| |
| checkAWTThread(); |
| synchronized (pageName2Page) { |
| pageName2Page.put(displayName, new WeakReference<Page>(page)); |
| } |
| } |
| |
| /** |
| * Get a page in the map given it's key. This is a basic lookup table. |
| * @param displayName String or associated key. |
| * @return the Page that is associated with the given key. |
| */ |
| protected Page getPageName2Page(String displayName) { |
| |
| |
| checkAWTThread(); |
| if (displayName == null) { |
| throw new NullPointerException("Displayname should not be null. You may be using this method incorrectly."); |
| } |
| // assert displayName.length() != 0; |
| synchronized (pageName2Page) { |
| /* |
| * Begin Test |
| */ |
| /* Page pageNode = pageName2Page.remove(displayName); |
| if (pageNode != null) { |
| Page pageNode2 = pageName2Page.get(displayName); |
| if (pageNode2 != null) { |
| throw new RuntimeException("Why are there two of the same page?: " + displayName + "\n PageNode1: " + pageNode + "\n PageNode2:" + pageNode2); |
| } |
| putPageName2Page(displayName, pageNode); |
| } */ |
| /* |
| * End Test |
| */ |
| Page page = null; |
| WeakReference<Page> pageRef = pageName2Page.get(displayName); |
| if (pageRef != null) { |
| page = pageRef.get(); |
| } |
| return page; |
| } |
| } |
| |
| /* This methods makes sure that the call if from the AWT Thread. |
| * If not it will dump the thread stack |
| */ |
| private void checkAWTThread() { |
| if (!SwingUtilities.isEventDispatchThread()) { |
| Thread.dumpStack(); |
| throw new RuntimeException("Not a Dispatched Thread"); |
| } |
| } |
| |
| /** |
| * Rename all references to a given page int eh faces config file. |
| * @param oldName String old name, if null thrown npe. |
| * @param newName String new name, if null thrown npe. |
| */ |
| public void renamePageInModel(String oldName, String newName) { |
| FacesModelUtility.renamePageInModel(configModel, oldName, newName); |
| } |
| |
| /** |
| * Remove page from the scene. |
| * @param pageNode |
| */ |
| public void removeSceneNodeEdges(Page pageNode) { |
| |
| Collection<NavigationCaseEdge> navCaseNodes = view.getNodeEdges(pageNode); |
| for (NavigationCaseEdge navCaseNode : navCaseNodes) { |
| try { |
| navCaseNode.destroy(); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } |
| // view.removeEdge(navCaseNode); |
| } |
| } |
| |
| /** |
| * Remove all rules and cases with this pagename. |
| * @param displayName |
| */ |
| public void removePageInModel(String displayName) { |
| configModel.startTransaction(); |
| FacesConfig facesConfig = configModel.getRootComponent(); |
| List<NavigationRule> navRules = facesConfig.getNavigationRules(); |
| for (NavigationRule navRule : navRules) { |
| String fromViewId = FacesModelUtility.getFromViewIdFiltered(navRule); |
| if (fromViewId != null && fromViewId.equals(displayName)) { |
| //if the rule is removed, don't check the cases. |
| facesConfig.removeNavigationRule(navRule); |
| } else { |
| List<NavigationCase> navCases = navRule.getNavigationCases(); |
| for (NavigationCase navCase : navCases) { |
| // String toViewId = navCase.getToViewId(); |
| String toViewId = FacesModelUtility.getToViewIdFiltered(navCase); |
| if (toViewId != null && toViewId.equals(displayName)) { |
| navRule.removeNavigationCase(navCase); |
| } |
| } |
| } |
| } |
| |
| try { |
| configModel.endTransaction(); |
| configModel.sync(); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } catch (IllegalStateException ise) { |
| Exceptions.printStackTrace(ise); |
| } |
| } |
| |
| /** |
| * Gets the WebFolder which contains the jsp pages. |
| * @return FileObject webfolder |
| */ |
| public FileObject getWebFolder() { |
| // assert webFolder.isValid(); |
| return webFolder; |
| } |
| |
| public boolean isPageInAnyFacesConfig(String name) { |
| WebModule webModule = WebModule.getWebModule(getWebFolder()); |
| FileObject[] configFiles = ConfigurationUtils.getFacesConfigFiles(webModule); |
| for (FileObject aConfigFile : configFiles) { |
| JSFConfigModel aConfigModel = ConfigurationUtils.getConfigModel(aConfigFile, true); |
| List<NavigationRule> rules = aConfigModel.getRootComponent().getNavigationRules(); |
| Collection<String> pagesInConfig = getFacesConfigPageNames(rules); |
| if (pagesInConfig.contains(name)) { |
| return true; /* Return as soon as you find one. */ |
| } |
| } |
| return false; |
| } |
| |
| public boolean isNavCaseInFacesConfig(NavigationCaseEdge navEdge) { |
| NavigationCase navCase = getNavCase2NavCaseEdge(navEdge); |
| JSFConfigComponent navRule = navCase.getParent(); |
| if (configModel.getRootComponent().getNavigationRules().contains(navRule)) { |
| return true; |
| } |
| return false; |
| } |
| |
| public void changeToAbstractNode(Page oldNode, String displayName) { |
| //1. Make Old Node an abstract node |
| Node tmpNode = new AbstractNode(Children.LEAF); |
| tmpNode.setName(displayName); |
| oldNode.replaceWrappedNode(tmpNode); //Does this take care of pageName2Node? |
| view.resetNodeWidget(oldNode, true); |
| } |
| |
| public DataObject getConfigDataObject() { |
| return configDataObj; |
| } |
| |
| public void saveLocation(String oldDisplayName, String newDisplayName) { |
| view.saveLocation(oldDisplayName, newDisplayName); |
| } |
| |
| /* WebFiles Wrappers */ |
| public final boolean removeWebFile(FileObject fileObj) { |
| return webFiles.remove(fileObj); |
| } |
| |
| /* WebFile Wrapper that adds a file to the webFile collection */ |
| public final boolean addWebFile(FileObject fileObj) { |
| return webFiles.add(fileObj); |
| } |
| |
| public final boolean containsWebFile(FileObject fileObj) { |
| return webFiles.contains(fileObj); |
| } |
| |
| public final void putNavCase2NavCaseEdge(NavigationCase navCase, NavigationCaseEdge navCaseEdge) { |
| navCase2NavCaseEdge.put(navCase, navCaseEdge); |
| } |
| |
| public final NavigationCaseEdge getNavCase2NavCaseEdge(NavigationCase navCase) { |
| return navCase2NavCaseEdge.get(navCase); |
| } |
| |
| private final NavigationCase getNavCase2NavCaseEdge(NavigationCaseEdge navEdge) { |
| Set<Entry<NavigationCase, NavigationCaseEdge>> entries = navCase2NavCaseEdge.entrySet(); |
| for (Entry entry : entries) { |
| if (entry.getValue().equals(navEdge)) { |
| return (NavigationCase) entry.getKey(); |
| } |
| } |
| return null; |
| } |
| |
| public final NavigationCaseEdge removeNavCase2NavCaseEdge(NavigationCase navCase) { |
| return navCase2NavCaseEdge.remove(navCase); |
| } |
| |
| //NavRule2String wrappers |
| public final String removeNavRule2String(NavigationRule navRule) { |
| return navRule2String.remove(navRule); |
| } |
| |
| public final String putNavRule2String(NavigationRule navRule, String navRuleName) { |
| return navRule2String.put(navRule, navRuleName); |
| } |
| |
| public PageFlowView getView() { |
| return view; |
| } |
| |
| public void setModelNavigationCaseName(NavigationCase navCase, String newName) { |
| configModel.startTransaction(); |
| |
| //By default check from outcome first. Maybe this should be the expectation. |
| if (navCase.getFromOutcome() != null) { |
| navCase.setFromOutcome(newName); |
| } |
| if (navCase.getFromAction() != null) { |
| navCase.setFromAction(newName); |
| } |
| |
| try { |
| configModel.endTransaction(); |
| configModel.sync(); |
| } catch (IOException ex) { |
| Exceptions.printStackTrace(ex); |
| } catch (IllegalStateException ise) { |
| Exceptions.printStackTrace(ise); |
| } |
| } |
| |
| public void removeModelNavigationCase(NavigationCase navCase) throws IOException { |
| configModel.startTransaction(); |
| NavigationRule navRule = (NavigationRule) navCase.getParent(); |
| if (navRule != null && navRule.getNavigationCases().contains(navCase)) { |
| //Only delete if it is still valid. |
| navRule.removeNavigationCase(navCase); |
| if (navRule.getNavigationCases().size() < 1) { |
| configModel.removeChildComponent(navRule); //put this back once you remove hack |
| } |
| } |
| |
| try { |
| configModel.endTransaction(); |
| } catch (IllegalStateException ise) { |
| Exceptions.printStackTrace(ise); |
| } |
| configModel.sync(); |
| } |
| |
| public void serializeNodeLocations() { |
| if (view != null && configDataObj !=null) { |
| view.serializeNodeLocations(PageFlowView.getStorageFile(configDataObj.getPrimaryFile())); |
| } else { |
| LOGGER.log(Level.WARNING, "Either Page Flow TopComponent of Faces Config DataObject is null" ); //NOI18N |
| } |
| } |
| |
| public void openNavigationCase(NavigationCaseEdge navCaseEdge) { |
| |
| final NavigationCase navCase = getNavCase2NavCaseEdge(navCaseEdge); |
| if (navCase == null) { |
| // XXX #152419 Possible NPE. |
| log("There is null NavigationCase for NavigationCaseEdge, navCaseEdge=" + navCaseEdge); // NOI18N |
| return; |
| } |
| |
| //FileObject fobj = NbEditorUtilities.getFileObject(navCase.getModel().getDocument()); |
| //DataObject dobj = DataObject.find(fobj); |
| DataObject dobj = getConfigDataObject(); |
| if (dobj != null) { |
| final EditCookie ec2 = dobj.getCookie(EditCookie.class); |
| if (ec2 != null) { |
| |
| final EditorCookie.Observable ec = dobj.getCookie(EditorCookie.Observable.class); |
| if (ec != null) { |
| StatusDisplayer.getDefault().setStatusText("otvirani"); // NOI18N |
| EventQueue.invokeLater(new Runnable() { |
| |
| public void run() { |
| |
| ec2.edit(); |
| JEditorPane[] panes = ec.getOpenedPanes(); |
| if (panes != null && panes.length > 0) { |
| openPane(panes[0], navCase); |
| //ec.open(); |
| } else { |
| ec.addPropertyChangeListener(new PropertyChangeListener() { |
| |
| public void propertyChange(PropertyChangeEvent evt) { |
| if (EditorCookie.Observable.PROP_OPENED_PANES.equals(evt.getPropertyName())) { |
| final JEditorPane[] panes = ec.getOpenedPanes(); |
| if (panes != null && panes.length > 0) { |
| openPane(panes[0], navCase); |
| } |
| ec.removePropertyChangeListener(this); |
| } |
| } |
| }); |
| ec.open(); |
| } |
| } |
| }); |
| } |
| } |
| } |
| } |
| |
| private void openPane(JEditorPane pane, NavigationCase navCase) { |
| final Cursor editCursor = pane.getCursor(); |
| pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR)); |
| pane.setCaretPosition(navCase.findPosition() + 2); |
| pane.setCursor(editCursor); |
| StatusDisplayer.getDefault().setStatusText(""); //NOI18N |
| } |
| |
| /** |
| * Moved this out of Page.java so that WebFolderListener also has an opportunity to |
| * access the providers so that it can listen and decide wether or not to update |
| * contents should be updated given a page. |
| **/ |
| public static final Collection<? extends PageContentModelProvider> getPageContentModelProviders() { |
| Lookup.Template<PageContentModelProvider> templ = new Lookup.Template<PageContentModelProvider>(PageContentModelProvider.class); |
| final Lookup.Result<PageContentModelProvider> result = Lookup.getDefault().lookup(templ); |
| Collection<? extends PageContentModelProvider> impls = result.allInstances(); |
| return impls; |
| } |
| |
| static class TestAccessor { |
| |
| static Collection<String> getPagesInFacesConfig(final PageFlowController controller) { |
| Set<NavigationRule> rules = TestAccessor.getAllNavigationRules(controller); |
| return controller.getFacesConfigPageNames(rules); |
| } |
| |
| static Collection<FileObject> getAllRelevantFiles(PageFlowController controller) { |
| return controller.getAllProjectRelevantFilesObjects(); |
| } |
| |
| static Set<NavigationRule> getAllNavigationRules(PageFlowController controller) { |
| return controller.navRule2String.keySet(); |
| } |
| |
| static Set<NavigationCase> getAllNavigationCases(PageFlowController controller) { |
| return controller.navCase2NavCaseEdge.keySet(); |
| } |
| } |
| |
| |
| private static void log(String message) { |
| Logger.getLogger(PageFlowController.class.getName()).log(Level.INFO, message); |
| } |
| } |