blob: 88973774187aa4a38b8145757f8179f846f6cd41 [file] [log] [blame]
/*
* 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);
}
}