0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041: package org.netbeans.modules.web.jsf.navigation;
0042:
0043: import java.awt.Cursor;
0044: import java.awt.Dialog;
0045: import java.awt.EventQueue;
0046: import java.beans.PropertyChangeEvent;
0047: import java.beans.PropertyChangeListener;
0048: import java.io.IOException;
0049: import java.lang.ref.WeakReference;
0050: import java.lang.reflect.InvocationTargetException;
0051: import javax.swing.SwingUtilities;
0052: import org.netbeans.modules.web.jsf.api.ConfigurationUtils;
0053: import org.netbeans.modules.web.jsf.api.editor.JSFConfigEditorContext;
0054: import org.netbeans.modules.web.jsf.api.facesmodel.FacesConfig;
0055: import org.netbeans.modules.web.jsf.api.facesmodel.JSFConfigModel;
0056: import org.netbeans.modules.web.jsf.api.facesmodel.NavigationCase;
0057: import org.netbeans.modules.web.jsf.api.facesmodel.NavigationRule;
0058: import org.openide.filesystems.FileObject;
0059: import org.openide.filesystems.FileStateInvalidException;
0060: import org.openide.loaders.DataObject;
0061: import org.openide.loaders.DataObjectNotFoundException;
0062: import org.openide.nodes.AbstractNode;
0063: import org.openide.nodes.Children;
0064: import org.openide.nodes.Node;
0065: import org.openide.util.Exceptions;
0066: import org.openide.filesystems.FileChangeListener;
0067: import java.util.*;
0068: import java.util.Map.Entry;
0069: import java.util.logging.Level;
0070: import java.util.logging.LogRecord;
0071: import java.util.logging.Logger;
0072: import java.util.prefs.Preferences;
0073: import javax.swing.JEditorPane;
0074: import org.netbeans.modules.web.api.webmodule.WebModule;
0075: import org.netbeans.modules.web.jsf.api.facesmodel.JSFConfigComponent;
0076: import org.openide.DialogDescriptor;
0077: import org.openide.DialogDisplayer;
0078: import org.openide.util.NbBundle;
0079: import org.netbeans.modules.web.jsf.navigation.PageFlowToolbarUtilities.Scope;
0080: import org.netbeans.modules.web.jsf.navigation.pagecontentmodel.PageContentModelProvider;
0081: import org.openide.NotifyDescriptor;
0082: import org.openide.awt.StatusDisplayer;
0083: import org.openide.cookies.EditorCookie;
0084: import org.openide.util.NbPreferences;
0085: import org.openide.cookies.EditCookie;
0086: import org.openide.filesystems.FileSystem;
0087: import org.openide.util.Lookup;
0088:
0089: /**
0090: *
0091: * @author joelle lam
0092: */
0093: public class PageFlowController {
0094:
0095: private PageFlowView view;
0096: private JSFConfigModel configModel;
0097: private DataObject configDataObj;
0098: private final Map<NavigationCase, NavigationCaseEdge> navCase2NavCaseEdge = new WeakHashMap<NavigationCase, NavigationCaseEdge>();
0099: private final Map<NavigationRule, String> navRule2String = new WeakHashMap<NavigationRule, String>();
0100: private final HashMap<String, WeakReference<Page>> pageName2Page = new HashMap<String, WeakReference<Page>>(); //Should this be synchronized.
0101: // public static final String DEFAULT_DOC_BASE_FOLDER = "web"; //NOI18NF
0102: private static final String NO_WEB_FOLDER_WARNING = NbBundle
0103: .getMessage(PageFlowController.class, "MSG_NoWebFolder");
0104: private static final String NO_WEB_FOLDER_TITLE = NbBundle
0105: .getMessage(PageFlowController.class, "TLE_NoWebFolder");
0106: private FileObject webFolder;
0107:
0108: /** Creates a new instance of PageFlowController
0109: * @param context
0110: * @param view
0111: */
0112: public PageFlowController(JSFConfigEditorContext context,
0113: PageFlowView view) {
0114: this .view = view;
0115: FileObject configFile = context.getFacesConfigFile();
0116:
0117: try {
0118: configDataObj = DataObject.find(configFile);
0119: } catch (DataObjectNotFoundException donfe) {
0120: Exceptions.printStackTrace(donfe);
0121: }
0122: configModel = ConfigurationUtils.getConfigModel(configFile,
0123: true);
0124:
0125: assert configModel != null;
0126: // Project project = FileOwnerQuery.getOwner(configFile);
0127: // webFolder = project.getProjectDirectory().getFileObject(DEFAULT_DOC_BASE_FOLDER);
0128: webFolder = PageFlowView.getWebFolder(configFile);
0129: webFiles = setupWebFiles(webFolder);
0130: }
0131:
0132: private Collection<FileObject> webFiles;
0133:
0134: private Collection<FileObject> setupWebFiles(FileObject webFolder) {
0135: Collection<FileObject> myWebFiles;
0136: if (webFolder == null) {
0137: ifNecessaryShowNoWebFolderDialog();
0138: myWebFiles = new LinkedList<FileObject>();
0139: } else {
0140: myWebFiles = getAllProjectRelevantFilesObjects();
0141: }
0142: return myWebFiles;
0143: }
0144:
0145: protected void ifNecessaryShowNoWebFolderDialog() {
0146: if (isShowNoWebFolderDialog()) {
0147:
0148: final NotWebFolder panel = new NotWebFolder(
0149: NO_WEB_FOLDER_WARNING);
0150: DialogDescriptor descriptor = new DialogDescriptor(panel,
0151: NO_WEB_FOLDER_TITLE, true,
0152: NotifyDescriptor.PLAIN_MESSAGE,
0153: NotifyDescriptor.YES_OPTION, null);
0154: descriptor.setMessageType(NotifyDescriptor.PLAIN_MESSAGE);
0155: descriptor
0156: .setClosingOptions(new Object[] { NotifyDescriptor.OK_OPTION });
0157: descriptor.setOptionsAlign(DialogDescriptor.BOTTOM_ALIGN);
0158: final Dialog d = DialogDisplayer.getDefault().createDialog(
0159: descriptor);
0160: d.setSize(380, 180);
0161: d.setVisible(true);
0162:
0163: setShowNoWebFolderDialog(panel.getShowDialog());
0164: }
0165: }
0166:
0167: public void destroy() {
0168: webFolder = null;
0169: configModel = null;
0170: view = null;
0171: webFiles.clear();
0172: navCase2NavCaseEdge.clear();
0173: navRule2String.clear();
0174: pageName2Page.clear();
0175:
0176: }
0177:
0178: private static final String PROP_SHOW_NO_WEB_FOLDER = "showNoWebFolder"; // NOI18N
0179:
0180: public final void setShowNoWebFolderDialog(boolean show) {
0181: getPreferences().putBoolean(PROP_SHOW_NO_WEB_FOLDER, show);
0182: }
0183:
0184: private static Preferences getPreferences() {
0185: return NbPreferences.forModule(PageFlowController.class);
0186: }
0187:
0188: public final boolean isShowNoWebFolderDialog() {
0189: return getPreferences().getBoolean(PROP_SHOW_NO_WEB_FOLDER,
0190: true);
0191: }
0192:
0193: private PropertyChangeListener pcl;
0194: private FileChangeListener fcl;
0195:
0196: // private ComponentListener cl;
0197: public void registerListeners() {
0198: if (pcl == null) {
0199: pcl = new FacesModelPropertyChangeListener(this );
0200: if (configModel != null) {
0201: configModel.addPropertyChangeListener(pcl);
0202: }
0203: }
0204:
0205: FileObject myWebFolder = getWebFolder();
0206: if (fcl == null) {
0207: fcl = new WebFolderListener(this );
0208: if (myWebFolder != null) {
0209: try {
0210: FileSystem fileSystem = myWebFolder.getFileSystem();
0211: fileSystem.addFileChangeListener(fcl);
0212:
0213: } catch (FileStateInvalidException ex) {
0214: Exceptions.printStackTrace(ex);
0215: }
0216: }
0217: }
0218: }
0219:
0220: /**
0221: * Unregister any listeners.
0222: */
0223: public void unregisterListeners() {
0224:
0225: if (pcl != null) {
0226: if (configModel != null) {
0227: configModel.removePropertyChangeListener(pcl);
0228: }
0229: pcl = null;
0230: }
0231:
0232: FileObject myWebFolder = getWebFolder();
0233: if (fcl != null && myWebFolder != null) {
0234: try {
0235: FileSystem fileSystem = myWebFolder.getFileSystem();
0236: fileSystem.removeFileChangeListener(fcl);
0237: fcl = null;
0238: } catch (FileStateInvalidException ex) {
0239: Exceptions.printStackTrace(ex);
0240: }
0241: }
0242: }
0243:
0244: FileChangeListener getFCL() {
0245: return fcl;
0246: }
0247:
0248: void flushGraphIfDirty() {
0249:
0250: if (isFilesDirty) {
0251: webFiles = setupWebFiles(webFolder);
0252: isFilesDirty = false;
0253: }
0254: if (isGraphDirty) {
0255: if (isWellFormed) {
0256: EventQueue.invokeLater(new Runnable() {
0257:
0258: public void run() {
0259: view.removeUserMalFormedFacesConfig(); // Does clear graph take care of this?
0260: setupGraph();
0261: }
0262: });
0263: } else {
0264: EventQueue.invokeLater(new Runnable() {
0265:
0266: public void run() {
0267: view.clearGraph();
0268: view.warnUserMalFormedFacesConfig();
0269: }
0270: });
0271:
0272: }
0273: isGraphDirty = false;
0274: }
0275: }
0276:
0277: private boolean isWellFormed = true;
0278: private boolean isGraphDirty = false;
0279: private boolean isFilesDirty = false;
0280:
0281: protected void setGraphDirtyWellFormed(boolean isWellFormed) {
0282: isGraphDirty = true;
0283: this .isWellFormed = isWellFormed;
0284: }
0285:
0286: protected void setGraphDirty() {
0287: isGraphDirty = true;
0288: }
0289:
0290: protected void setFilesDirty() {
0291: this .isFilesDirty = true;
0292: isGraphDirty = true;
0293: }
0294:
0295: public boolean isCurrentScope(Scope scope) {
0296: return PageFlowToolbarUtilities.getInstance(view)
0297: .getCurrentScope().equals(scope);
0298: }
0299:
0300: /**
0301: * Creates a Link in the FacesConfiguration
0302: * @param source from page, if null an NPE will be thrown.
0303: * @param target to page, if null an NPE will be thrown.
0304: * @param pinNode if null then it was not conntect to a pin.
0305: * @return
0306: */
0307: public NavigationCase createLink(Page source, Page target,
0308: Pin pinNode) {
0309: if (source == null) {
0310: throw new NullPointerException(
0311: "Source page should not be null.");
0312: } else if (target == null) {
0313: throw new NullPointerException(
0314: "Target page should not be null");
0315: }
0316:
0317: String sourceName = source.getDisplayName();
0318: int caseNum = 1;
0319:
0320: configModel.startTransaction();
0321: FacesConfig facesConfig = configModel.getRootComponent();
0322: NavigationRule navRule = getRuleWithFromViewID(facesConfig,
0323: source.getDisplayName());
0324: NavigationCase navCase = configModel.getFactory()
0325: .createNavigationCase();
0326: if (navRule == null) {
0327: navRule = configModel.getFactory().createNavigationRule();
0328: FacesModelUtility.setFromViewId(navRule, source
0329: .getDisplayName());
0330: facesConfig.addNavigationRule(navRule);
0331: navRule2String.put(navRule, FacesModelUtility
0332: .getFromViewIdFiltered(navRule));
0333: } else {
0334: caseNum = getNewCaseNumber(navRule);
0335: }
0336: String caseName = CASE_STRING + Integer.toString(caseNum);
0337:
0338: if (pinNode != null) {
0339: pinNode.setFromOutcome(caseName);
0340: }
0341: navCase.setFromOutcome(caseName);
0342:
0343: FacesModelUtility.setToViewId(navCase, target.getDisplayName());
0344: navRule.addNavigationCase(navCase);
0345:
0346: configModel.endTransaction();
0347: try {
0348: configModel.sync();
0349: } catch (IOException ex) {
0350: Exceptions.printStackTrace(ex);
0351: }
0352:
0353: return navCase;
0354: }
0355:
0356: public void updatePageItems(Page pageNode) {
0357: view.resetNodeWidget(pageNode, true);
0358: view.validateGraph();
0359: }
0360:
0361: private static final String CASE_STRING = "case";
0362:
0363: private int getNewCaseNumber(NavigationRule navRule) {
0364: Collection<String> caseOutcomes = new HashSet<String>();
0365: List<NavigationCase> navCases = navRule.getNavigationCases();
0366: for (NavigationCase navCase : navCases) {
0367: caseOutcomes.add(navCase.getFromOutcome());
0368: // caseOutcomes.add(navCase.getFromAction());
0369: }
0370:
0371: int caseNum = 1;
0372: while (true) {
0373: if (!caseOutcomes.contains(CASE_STRING
0374: + Integer.toString(caseNum))) {
0375: return caseNum;
0376: }
0377: caseNum++;
0378: }
0379: }
0380:
0381: /**
0382: * @return the navigation rule. This will be null if none was found
0383: **/
0384: private NavigationRule getRuleWithFromViewID(
0385: FacesConfig facesConfig, String fromViewId) {
0386:
0387: for (NavigationRule navRule : facesConfig.getNavigationRules()) {
0388: String rulefromViewId = FacesModelUtility
0389: .getFromViewIdFiltered(navRule);
0390: if (rulefromViewId != null
0391: && rulefromViewId.equals(fromViewId)) {
0392: // Match Found
0393: return navRule;
0394: }
0395: }
0396:
0397: return null;
0398: }
0399:
0400: private final Collection<FileObject> getAllProjectRelevantFilesObjects() {
0401: return getProjectKnownFileOjbects(getWebFolder());
0402: }
0403:
0404: private Collection<FileObject> getProjectKnownFileOjbects(
0405: FileObject folder) {
0406: Collection<FileObject> projectKnownFiles = new LinkedList<FileObject>();
0407:
0408: FileObject[] childrenFiles = new FileObject[] {};
0409: if (folder != null) {
0410: childrenFiles = folder.getChildren();
0411: }
0412: for (FileObject file : childrenFiles) {
0413: if (!file.isFolder()) {
0414: if (isKnownFile(file)) {
0415: projectKnownFiles.add(file);
0416: }
0417: } else if (isKnownFolder(file)) {
0418: projectKnownFiles
0419: .addAll(getProjectKnownFileOjbects(file));
0420: }
0421: }
0422:
0423: return projectKnownFiles;
0424: }
0425:
0426: /**
0427: * Check if the file type in known.
0428: * @param file the fileobject type to check. If null, throws NPE.
0429: * @return if it is of type jsp, jspf, or html it will return true.
0430: */
0431: public final boolean isKnownFile(FileObject file) {
0432: if (file.getMIMEType().equals("text/x-jsp")
0433: && !file.getExt().equals("jspf")) {
0434: return true;
0435: } else if (file.getMIMEType().equals("text/html")) {
0436: return true;
0437: }
0438: return false;
0439: }
0440:
0441: public final boolean isKnownFolder(FileObject folder) {
0442: /* If it is not a folder return false*/
0443: if (!folder.isFolder()) {
0444: return false;
0445: }
0446: /* If it does not exist within WebFolder return false */
0447: if (!folder.getPath().contains(getWebFolder().getPath())) {
0448: return false;
0449: }
0450: /* If it exists withing WEB-INF or META-INF return false */
0451: if (folder.getPath().contains("WEB-INF")
0452: || folder.getPath().contains("META-INF")) {
0453: return false;
0454: }
0455: return true;
0456: }
0457:
0458: /**
0459: * Setup The Graph
0460: * Should only be called by init();
0461: *
0462: **/
0463: public boolean setupGraph() {
0464: view.saveLocations();
0465: return setupGraphNoSaveData();
0466: }
0467:
0468: private PropertyChangeListener otherFacesConfigListener = null;
0469:
0470: private PropertyChangeListener getOtherFacesConfigListener() {
0471: if (otherFacesConfigListener == null) {
0472: return new OtherFacesModelListener();
0473: }
0474: return otherFacesConfigListener;
0475: }
0476:
0477: private void removeOtherFacesConfigListener() {
0478: WebModule webModule = WebModule.getWebModule(getWebFolder());
0479: FileObject[] configFiles = ConfigurationUtils
0480: .getFacesConfigFiles(webModule);
0481: for (FileObject aConfigFile : configFiles) {
0482: JSFConfigModel aConfigModel = ConfigurationUtils
0483: .getConfigModel(aConfigFile, true);
0484: aConfigModel
0485: .removePropertyChangeListener(otherFacesConfigListener);
0486: }
0487: otherFacesConfigListener = null;
0488: }
0489:
0490: protected void releaseGraphInfo() {
0491: /* This listener is only created when it was a All_FACES scope */
0492: if (otherFacesConfigListener != null) {
0493: removeOtherFacesConfigListener();
0494: }
0495:
0496: view.clearGraph();
0497: clearPageName2Page();
0498: navCase2NavCaseEdge.clear();
0499: navRule2String.clear();
0500:
0501: }
0502:
0503: public boolean setupGraphNoSaveData() {
0504: LOGGER.entering(PageFlowController.class.toString(),
0505: "setupGraphNoSaveData()");
0506:
0507: assert configModel != null;
0508: // assert webFolder != null;
0509: assert webFiles != null;
0510: releaseGraphInfo();
0511:
0512: FacesConfig facesConfig = configModel.getRootComponent();
0513:
0514: if (facesConfig == null) {
0515: return false;
0516: }
0517:
0518: /* If the most recently saved xml doc is malformed, we should know about it through this try statement. */
0519: try {
0520: List<NavigationRule> rules = null;
0521: if (isCurrentScope(Scope.SCOPE_FACESCONFIG)) {
0522: rules = facesConfig.getNavigationRules();
0523: for (NavigationRule navRule : rules) {
0524: navRule2String.put(navRule, FacesModelUtility
0525: .getFromViewIdFiltered(navRule));
0526: }
0527: Collection<String> pagesInConfig = getFacesConfigPageNames(rules);
0528: createFacesConfigPages(pagesInConfig);
0529: } else if (isCurrentScope(Scope.SCOPE_PROJECT)) {
0530: rules = facesConfig.getNavigationRules();
0531: for (NavigationRule navRule : rules) {
0532: navRule2String.put(navRule, FacesModelUtility
0533: .getFromViewIdFiltered(navRule));
0534: }
0535: Collection<String> pagesInConfig = getFacesConfigPageNames(rules);
0536: createAllProjectPages(pagesInConfig);
0537: } else if (isCurrentScope(Scope.SCOPE_ALL_FACESCONFIG)) {
0538: List<NavigationRule> allRules = new ArrayList<NavigationRule>();
0539: FileObject myWebFolder = getWebFolder();
0540: if (myWebFolder != null) {
0541: WebModule webModule = WebModule
0542: .getWebModule(myWebFolder);
0543: FileObject[] configFiles = ConfigurationUtils
0544: .getFacesConfigFiles(webModule);
0545: for (FileObject aConfigFile : configFiles) {
0546: JSFConfigModel aConfigModel = ConfigurationUtils
0547: .getConfigModel(aConfigFile, true);
0548: allRules.addAll(aConfigModel.getRootComponent()
0549: .getNavigationRules());
0550: if (!configModel.equals(aConfigModel)) {
0551: aConfigModel
0552: .addPropertyChangeListener(getOtherFacesConfigListener());
0553: }
0554: }
0555: for (NavigationRule navRule : allRules) {
0556: navRule2String.put(navRule, FacesModelUtility
0557: .getFromViewIdFiltered(navRule));
0558: }
0559: Collection<String> pagesInConfig = getFacesConfigPageNames(allRules);
0560: createFacesConfigPages(pagesInConfig);
0561: rules = allRules;
0562: } else {
0563: /* If no web module exists don't worry about other faces-config files */
0564: rules = facesConfig.getNavigationRules();
0565: for (NavigationRule navRule : rules) {
0566: navRule2String.put(navRule, FacesModelUtility
0567: .getFromViewIdFiltered(navRule));
0568: }
0569: Collection<String> pagesInConfig = getFacesConfigPageNames(rules);
0570: createAllProjectPages(pagesInConfig);
0571: }
0572: }
0573: createAllEdges(rules);
0574: view.validateGraph();
0575: LOGGER.log(new LogRecord(Level.FINE,
0576: "PageFlowEditor # Rules: " + rules.size() + "\n"
0577: + " # WebPages: "
0578: + webFiles.size() + "\n"
0579: + " # TotalPages: "
0580: + pageName2Page.size()));
0581: } catch (IllegalStateException ise) {
0582: view.warnUserMalFormedFacesConfig();
0583: view.validateGraph();
0584: LOGGER.log(new LogRecord(Level.FINE,
0585: "Illegal SateException thrown: " + ise.toString()));
0586: }
0587: LOGGER.exiting(PageFlowController.class.toString(),
0588: "setupGraphNoSaveData()");
0589: return true;
0590: }
0591:
0592: private class OtherFacesModelListener implements
0593: PropertyChangeListener {
0594:
0595: public void propertyChange(PropertyChangeEvent evt) {
0596:
0597: EventQueue.invokeLater(new Runnable() {
0598:
0599: public void run() {
0600:
0601: setupGraph();
0602: }
0603: });
0604: }
0605: }
0606:
0607: private void createAllEdges(List<NavigationRule> rules) {
0608:
0609: List<NavigationRule> editableRules = configModel
0610: .getRootComponent().getNavigationRules();
0611: for (NavigationRule rule : rules) {
0612: List<NavigationCase> navCases = rule.getNavigationCases();
0613:
0614: /* this is for ALL_FACES_CONFIG scope*/
0615: boolean isModifableEdge = editableRules.contains(rule) ? true
0616: : false;
0617:
0618: for (NavigationCase navCase : navCases) {
0619:
0620: NavigationCaseEdge navEdge = new NavigationCaseEdge(
0621: this , navCase);
0622: navCase2NavCaseEdge.put(navCase, navEdge);
0623: navEdge.setModifiable(isModifableEdge);
0624: if (navEdge.getFromViewId() != null
0625: && navEdge.getToViewId() != null) {
0626: createEdge(navEdge);
0627: }
0628: }
0629: }
0630: }
0631:
0632: /**
0633: * Creates and edge in the scene, this method does not add an reference in
0634: * the faces configuration. In general it is best to call createLink
0635: * as that will call createEdge indirectly through the faces model listener.
0636: * @param caseNode a NavigationCaseEdge. If null, will throw NPE.
0637: */
0638: protected void createEdge(NavigationCaseEdge caseNode) {
0639: String fromPage = caseNode.getFromViewId();
0640: String toPage = caseNode.getToViewId();
0641: if (getPageName2Page(fromPage) == null
0642: || getPageName2Page(toPage) == null) {
0643: System.err.println("Why is this node null? CaseNode: "
0644: + caseNode);
0645: System.err.println("FromPage: " + fromPage);
0646: System.err.println("ToPage: " + toPage);
0647: Thread.dumpStack();
0648: } else {
0649: view.createEdge(caseNode, getPageName2Page(fromPage),
0650: getPageName2Page(toPage));
0651: }
0652: }
0653:
0654: private Collection<String> getFacesConfigPageNames(
0655: Collection<NavigationRule> navRules) {
0656: // Get all the pages in the faces config. But don't list them twice.
0657: Collection<String> pages = new HashSet<String>();
0658: for (NavigationRule navRule : navRules) {
0659: String pageName = FacesModelUtility
0660: .getFromViewIdFiltered(navRule);
0661: pages.add(pageName);
0662: Collection<NavigationCase> navCases = navRule
0663: .getNavigationCases();
0664: for (NavigationCase navCase : navCases) {
0665: // String toPage = navCase.getToViewId();
0666: String toPage = FacesModelUtility
0667: .getToViewIdFiltered(navCase);
0668: if (toPage != null) {
0669: pages.add(toPage);
0670: }
0671: }
0672: }
0673: return pages;
0674: }
0675:
0676: public java.util.Stack<String> PageFlowCreationStack = new java.util.Stack<String>();
0677: private int PageFlowCreationCount = 0;
0678:
0679: /**
0680: * Create a Page from a node
0681: * This method
0682: * does not actually add the pages to the scene. It just creates the
0683: * component. You will need to call scene.createNode(page) if you want.
0684: * @param node the node or dataobject node delegate of a given fileobject.
0685: * Use dataObject.find(fileObject).getNodeDelegate for the given
0686: * page. If no dataObject backing the page, call createPage(String)
0687: * @return page the Page that was created.
0688: */
0689: public Page createPage(Node node) {
0690: Page pageNode = new Page(this , node);
0691: Calendar rightNow = Calendar.getInstance();
0692: PageFlowCreationStack.push("\n" + PageFlowCreationCount + ". "
0693: + rightNow.get(Calendar.MINUTE) + ":"
0694: + rightNow.get(Calendar.SECOND) + " - " + pageNode);
0695: PageFlowCreationCount++;
0696: return pageNode;
0697: }
0698:
0699: /*
0700: * Create PageFlow from a string with no backing page. This method
0701: * does not actually add the pages to the scene. It just creates the
0702: * component. You will need to call scene.createNode(page) if you want
0703: * to add it to the scene.
0704: * @param name the string of the name of the page to create
0705: * If null is passed, NPE thrown.
0706: * If empty string assertion thrown and null returned.
0707: * @return page the Page that was created.
0708: */
0709: public Page createPage(String pageName) {
0710: Page node = null;
0711: if (pageName == null) {
0712: throw new NullPointerException("Page name string is null");
0713: }
0714: assert pageName.length() != 0;
0715: Node tmpNode = new AbstractNode(Children.LEAF);
0716: tmpNode.setName(pageName);
0717: node = createPage(tmpNode);
0718: return node;
0719: }
0720:
0721: public java.util.Stack<String> PageFlowDestroyStack = new java.util.Stack<String>();
0722: private int PageFlowDestroyCount = 0;
0723:
0724: /**
0725: * Destroys the page in the scene (removing the page content model and
0726: * the page content listeners). This odes not actual destroy the dataobject
0727: * or the backing file object.
0728: * @param page Page to be deleted.
0729: */
0730: private void destroyPageFlowNode(Page page) {
0731: if (page != null) {
0732: page.destroy2();
0733: Calendar rightNow = Calendar.getInstance();
0734: PageFlowDestroyStack.push("\n" + PageFlowDestroyCount
0735: + ". " + rightNow.get(Calendar.MINUTE) + ":"
0736: + rightNow.get(Calendar.SECOND) + " - " + page);
0737: PageFlowDestroyCount++;
0738: }
0739: }
0740:
0741: private void createAllProjectPages(Collection<String> pagesInConfig) {
0742:
0743: Collection<String> pages = new HashSet<String>(pagesInConfig);
0744:
0745: //Create all pages in the project...
0746: FileObject[] webFilesTmp = webFiles.toArray(new FileObject[0]);//Use copy because you may need to remove these files.
0747: for (FileObject webFile : webFilesTmp) {
0748: //DISPLAYNAME:
0749: String webFileName = Page.getFolderDisplayName(
0750: getWebFolder(), webFile);
0751: Page node = null;
0752: try {
0753: node = createPage((DataObject.find(webFile))
0754: .getNodeDelegate());
0755: view.createNode(node, null, null);
0756: //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.
0757: pages.remove(webFileName);
0758: } catch (DataObjectNotFoundException ex) {
0759: webFiles.remove(webFile); //Remove this file because it may have been deleted.
0760: }
0761: }
0762:
0763: //Create any pages that don't actually exist but are defined specified by the config file.
0764: for (String pageName : pages) {
0765: if (pageName != null) {
0766: Node tmpNode = new AbstractNode(Children.LEAF);
0767: tmpNode.setName(pageName);
0768: Page node = createPage(tmpNode);
0769: view.createNode(node, null, null);
0770: }
0771: }
0772: }
0773:
0774: /**
0775: * Givena pageName, look through the list of predefined webFiles and return the matching fileObject
0776: * @return FileObject for which the match was found or null of none was found.
0777: **/
0778: private FileObject getFileObject(String pageName) {
0779: for (FileObject webFile : webFiles) {
0780: //DISPLAYNAME:
0781: String webFileName = Page.getFolderDisplayName(
0782: getWebFolder(), webFile);
0783: // String webFileName = webFile.getNameExt();
0784: if (webFileName.equals(pageName)) {
0785: return webFile;
0786: }
0787: }
0788: return null;
0789: }
0790:
0791: private void createFacesConfigPages(Collection<String> pagesInConfig) {
0792: Collection<String> pages = new HashSet<String>(pagesInConfig);
0793:
0794: for (String pageName : pages) {
0795: if (pageName != null) {
0796: FileObject file = getFileObject(pageName);
0797: Node wrapNode = null;
0798: if (file == null) {
0799: wrapNode = new AbstractNode(Children.LEAF);
0800: wrapNode.setName(pageName);
0801: } else {
0802: try {
0803: wrapNode = (DataObject.find(file))
0804: .getNodeDelegate();
0805: } catch (DataObjectNotFoundException donfe) {
0806: Exceptions.printStackTrace(donfe);
0807: }
0808: }
0809: Page node = createPage(wrapNode);
0810: view.createNode(node, null, null);
0811: }
0812: }
0813: }
0814:
0815: private static final Logger LOGGER = Logger
0816: .getLogger(PageFlowController.class.getName());
0817:
0818: /**
0819: * Remove the page from the hashtable of string (or pages names ) to actual
0820: * pages. Use permDestroy value to destroy the page in the scene completely.
0821: * @param page that you want to remove.
0822: * @param permDestroy true - destroys the page in the scene (removing the
0823: * page content model and the page content listeners).
0824: * This does not actual destroy the dataobject
0825: * or the backing file object.
0826: * false - if you just want to remove it from the list
0827: * with the associated name.
0828: * @return page that was removed.
0829: */
0830: public Page removePageName2Page(Page page, boolean permDestroy) {
0831: return removePageName2Page(page.getDisplayName(), permDestroy);
0832: }
0833:
0834: /**
0835: * Refer to removePageName2Page(Page page, boolean permDestroy) for details
0836: * @param pageName the string value of the page name that you want removed.
0837: * @param destroy
0838: * @return
0839: */
0840: public Page removePageName2Page(String pageName, boolean permDestroy) {
0841: LOGGER.finest("PageName2Page: remove " + pageName);
0842: checkAWTThread();
0843: synchronized (pageName2Page) {
0844: Page node = null;
0845: WeakReference<Page> nodeRef = pageName2Page
0846: .remove(pageName);
0847: if (nodeRef != null) {
0848: node = nodeRef.get();
0849: if (permDestroy) {
0850: destroyPageFlowNode(node);
0851: }
0852: }
0853: return node;
0854: }
0855: }
0856:
0857: /**
0858: * Replace page name in PageName2Node HasMap. This is general used in a
0859: * page rename. In general this removes the old Page and add the new one with
0860: * the given name.
0861: * @param page Page that should be added into the map. If null, NPE thrown
0862: * and nothing removed from the map.
0863: * @param String newName String that you want to assign to the page.
0864: * @param String oldName String that was assigned to the page.
0865: * @return true if page was found to replace, false is page was not found.
0866: **/
0867: public boolean replacePageName2Page(Page page, String newName,
0868: String oldName) {
0869:
0870: LOGGER.finest("PageName2Page: replace " + oldName + " to "
0871: + newName);
0872: //assert (newName.length() > 0);
0873: //assert (oldName.length() > 0);
0874:
0875: if (page == null) {
0876: throw new NullPointerException("Page can not be null.");
0877: }
0878:
0879: checkAWTThread();
0880: synchronized (pageName2Page) {
0881: WeakReference<Page> page2Ref = pageName2Page
0882: .remove(oldName);
0883: if (page2Ref != null) {
0884: Page pageFound = page2Ref.get();
0885:
0886: if (pageFound != null) {
0887: LOGGER
0888: .finest("Trying to replace page in map, but page not found:"
0889: + page);
0890: }
0891: pageName2Page.put(newName,
0892: new WeakReference<Page>(page));
0893: return true;
0894: }
0895: return false;
0896: }
0897: }
0898:
0899: /**
0900: * Clears the pageName 2 Page mapping. Generally you want do this when you
0901: * are about to throw everything in the scene away. This keeps references
0902: * from being kept.
0903: */
0904: protected void clearPageName2Page() {
0905: LOGGER.finest("PageName2Page: clear");
0906: Set<String> keys;
0907: synchronized (pageName2Page) {
0908: keys = new HashSet<String>(pageName2Page.keySet());
0909: }
0910: for (String key : keys) {
0911: removePageName2Page(key, true);
0912: }
0913: }
0914:
0915: /**
0916: * Associate a page with a given string name for future reference. In general
0917: * this method is called by a Page object to add itself. Really no other classes
0918: * should use this method.
0919: * @param displayName name of the page you would like to reference it with (key)
0920: * displayName can not be an empty string.
0921: * @param page Page to be associated with the string. If null, NPE thrown.
0922: */
0923: protected void putPageName2Page(String displayName, Page page) {
0924:
0925: LOGGER.finest("PageName2Page: put " + displayName);
0926: //assert displayName.length() != 0;
0927: if (page == null) {
0928: throw new NullPointerException(
0929: "putPageName2Page does not accept null pages.");
0930: }
0931:
0932: checkAWTThread();
0933: synchronized (pageName2Page) {
0934: pageName2Page.put(displayName,
0935: new WeakReference<Page>(page));
0936: }
0937: }
0938:
0939: /**
0940: * Get a page in the map given it's key. This is a basic lookup table.
0941: * @param displayName String or associated key.
0942: * @return the Page that is associated with the given key.
0943: */
0944: protected Page getPageName2Page(String displayName) {
0945:
0946: checkAWTThread();
0947: if (displayName == null) {
0948: throw new NullPointerException(
0949: "Displayname should not be null. You may be using this method incorrectly.");
0950: }
0951: // assert displayName.length() != 0;
0952: synchronized (pageName2Page) {
0953: /*
0954: * Begin Test
0955: */
0956: /* Page pageNode = pageName2Page.remove(displayName);
0957: if (pageNode != null) {
0958: Page pageNode2 = pageName2Page.get(displayName);
0959: if (pageNode2 != null) {
0960: throw new RuntimeException("Why are there two of the same page?: " + displayName + "\n PageNode1: " + pageNode + "\n PageNode2:" + pageNode2);
0961: }
0962: putPageName2Page(displayName, pageNode);
0963: } */
0964: /*
0965: * End Test
0966: */
0967: Page page = null;
0968: WeakReference<Page> pageRef = pageName2Page
0969: .get(displayName);
0970: if (pageRef != null) {
0971: page = pageRef.get();
0972: }
0973: return page;
0974: }
0975: }
0976:
0977: /* This methods makes sure that the call if from the AWT Thread.
0978: * If not it will dump the thread stack
0979: */
0980: private void checkAWTThread() {
0981: if (!SwingUtilities.isEventDispatchThread()) {
0982: Thread.dumpStack();
0983: throw new RuntimeException("Not a Dispatched Thread");
0984: }
0985: }
0986:
0987: /**
0988: * Rename all references to a given page int eh faces config file.
0989: * @param oldName String old name, if null thrown npe.
0990: * @param newName String new name, if null thrown npe.
0991: */
0992: public void renamePageInModel(String oldName, String newName) {
0993: FacesModelUtility.renamePageInModel(configModel, oldName,
0994: newName);
0995: }
0996:
0997: /**
0998: * Remove page from the scene.
0999: * @param pageNode
1000: */
1001: public void removeSceneNodeEdges(Page pageNode) {
1002:
1003: Collection<NavigationCaseEdge> navCaseNodes = view
1004: .getNodeEdges(pageNode);
1005: for (NavigationCaseEdge navCaseNode : navCaseNodes) {
1006: try {
1007: navCaseNode.destroy();
1008: } catch (IOException ex) {
1009: Exceptions.printStackTrace(ex);
1010: }
1011: // view.removeEdge(navCaseNode);
1012: }
1013: }
1014:
1015: /**
1016: * Remove all rules and cases with this pagename.
1017: * @param displayName
1018: */
1019: public void removePageInModel(String displayName) {
1020: configModel.startTransaction();
1021: FacesConfig facesConfig = configModel.getRootComponent();
1022: List<NavigationRule> navRules = facesConfig
1023: .getNavigationRules();
1024: for (NavigationRule navRule : navRules) {
1025: String fromViewId = FacesModelUtility
1026: .getFromViewIdFiltered(navRule);
1027: if (fromViewId != null && fromViewId.equals(displayName)) {
1028: //if the rule is removed, don't check the cases.
1029: facesConfig.removeNavigationRule(navRule);
1030: } else {
1031: List<NavigationCase> navCases = navRule
1032: .getNavigationCases();
1033: for (NavigationCase navCase : navCases) {
1034: // String toViewId = navCase.getToViewId();
1035: String toViewId = FacesModelUtility
1036: .getToViewIdFiltered(navCase);
1037: if (toViewId != null
1038: && toViewId.equals(displayName)) {
1039: navRule.removeNavigationCase(navCase);
1040: }
1041: }
1042: }
1043: }
1044:
1045: configModel.endTransaction();
1046: try {
1047: configModel.sync();
1048: } catch (IOException ex) {
1049: Exceptions.printStackTrace(ex);
1050: }
1051: }
1052:
1053: /**
1054: * Gets the WebFolder which contains the jsp pages.
1055: * @return FileObject webfolder
1056: */
1057: public FileObject getWebFolder() {
1058: // assert webFolder.isValid();
1059: return webFolder;
1060: }
1061:
1062: public boolean isPageInAnyFacesConfig(String name) {
1063: WebModule webModule = WebModule.getWebModule(getWebFolder());
1064: FileObject[] configFiles = ConfigurationUtils
1065: .getFacesConfigFiles(webModule);
1066: for (FileObject aConfigFile : configFiles) {
1067: JSFConfigModel aConfigModel = ConfigurationUtils
1068: .getConfigModel(aConfigFile, true);
1069: List<NavigationRule> rules = aConfigModel
1070: .getRootComponent().getNavigationRules();
1071: Collection<String> pagesInConfig = getFacesConfigPageNames(rules);
1072: if (pagesInConfig.contains(name)) {
1073: return true; /* Return as soon as you find one. */
1074: }
1075: }
1076: return false;
1077: }
1078:
1079: public boolean isNavCaseInFacesConfig(NavigationCaseEdge navEdge) {
1080: NavigationCase navCase = getNavCase2NavCaseEdge(navEdge);
1081: JSFConfigComponent navRule = navCase.getParent();
1082: if (configModel.getRootComponent().getNavigationRules()
1083: .contains(navRule)) {
1084: return true;
1085: }
1086: return false;
1087: }
1088:
1089: public void changeToAbstractNode(Page oldNode, String displayName) {
1090: //1. Make Old Node an abstract node
1091: Node tmpNode = new AbstractNode(Children.LEAF);
1092: tmpNode.setName(displayName);
1093: oldNode.replaceWrappedNode(tmpNode); //Does this take care of pageName2Node?
1094: view.resetNodeWidget(oldNode, true);
1095: }
1096:
1097: public DataObject getConfigDataObject() {
1098: return configDataObj;
1099: }
1100:
1101: public void saveLocation(String oldDisplayName,
1102: String newDisplayName) {
1103: view.saveLocation(oldDisplayName, newDisplayName);
1104: }
1105:
1106: /* WebFiles Wrappers */
1107: public final boolean removeWebFile(FileObject fileObj) {
1108: return webFiles.remove(fileObj);
1109: }
1110:
1111: /* WebFile Wrapper that adds a file to the webFile collection */
1112: public final boolean addWebFile(FileObject fileObj) {
1113: return webFiles.add(fileObj);
1114: }
1115:
1116: public final boolean containsWebFile(FileObject fileObj) {
1117: return webFiles.contains(fileObj);
1118: }
1119:
1120: public final void putNavCase2NavCaseEdge(NavigationCase navCase,
1121: NavigationCaseEdge navCaseEdge) {
1122: navCase2NavCaseEdge.put(navCase, navCaseEdge);
1123: }
1124:
1125: public final NavigationCaseEdge getNavCase2NavCaseEdge(
1126: NavigationCase navCase) {
1127: return navCase2NavCaseEdge.get(navCase);
1128: }
1129:
1130: private final NavigationCase getNavCase2NavCaseEdge(
1131: NavigationCaseEdge navEdge) {
1132: Set<Entry<NavigationCase, NavigationCaseEdge>> entries = navCase2NavCaseEdge
1133: .entrySet();
1134: for (Entry entry : entries) {
1135: if (entry.getValue().equals(navEdge)) {
1136: return (NavigationCase) entry.getKey();
1137: }
1138: }
1139: return null;
1140: }
1141:
1142: public final NavigationCaseEdge removeNavCase2NavCaseEdge(
1143: NavigationCase navCase) {
1144: return navCase2NavCaseEdge.remove(navCase);
1145: }
1146:
1147: //NavRule2String wrappers
1148: public final String removeNavRule2String(NavigationRule navRule) {
1149: return navRule2String.remove(navRule);
1150: }
1151:
1152: public final String putNavRule2String(NavigationRule navRule,
1153: String navRuleName) {
1154: return navRule2String.put(navRule, navRuleName);
1155: }
1156:
1157: public PageFlowView getView() {
1158: return view;
1159: }
1160:
1161: public void setModelNavigationCaseName(NavigationCase navCase,
1162: String newName) {
1163: configModel.startTransaction();
1164:
1165: //By default check from outcome first. Maybe this should be the expectation.
1166: if (navCase.getFromOutcome() != null) {
1167: navCase.setFromOutcome(newName);
1168: }
1169: if (navCase.getFromAction() != null) {
1170: navCase.setFromAction(newName);
1171: }
1172: configModel.endTransaction();
1173:
1174: try {
1175: configModel.sync();
1176: } catch (IOException ex) {
1177: Exceptions.printStackTrace(ex);
1178: }
1179: }
1180:
1181: public void removeModelNavigationCase(NavigationCase navCase)
1182: throws IOException {
1183: configModel.startTransaction();
1184: NavigationRule navRule = (NavigationRule) navCase.getParent();
1185: if (navRule != null
1186: && navRule.getNavigationCases().contains(navCase)) {
1187: //Only delete if it is still valid.
1188: navRule.removeNavigationCase(navCase);
1189: if (navRule.getNavigationCases().size() < 1) {
1190: configModel.removeChildComponent(navRule); //put this back once you remove hack
1191: }
1192: }
1193: configModel.endTransaction();
1194: configModel.sync();
1195: }
1196:
1197: public void serializeNodeLocations() {
1198: view.serializeNodeLocations(PageFlowView
1199: .getStorageFile(configDataObj.getPrimaryFile()));
1200: }
1201:
1202: public void openNavigationCase(NavigationCaseEdge navCaseEdge) {
1203:
1204: final NavigationCase navCase = getNavCase2NavCaseEdge(navCaseEdge);
1205: //FileObject fobj = NbEditorUtilities.getFileObject(navCase.getModel().getDocument());
1206: //DataObject dobj = DataObject.find(fobj);
1207: DataObject dobj = getConfigDataObject();
1208: if (dobj != null) {
1209: final EditCookie ec2 = dobj.getCookie(EditCookie.class);
1210: if (ec2 != null) {
1211:
1212: final EditorCookie.Observable ec = dobj
1213: .getCookie(EditorCookie.Observable.class);
1214: if (ec != null) {
1215: StatusDisplayer.getDefault().setStatusText(
1216: "otvirani"); // NOI18N
1217: EventQueue.invokeLater(new Runnable() {
1218:
1219: public void run() {
1220:
1221: ec2.edit();
1222: JEditorPane[] panes = ec.getOpenedPanes();
1223: if (panes != null && panes.length > 0) {
1224: openPane(panes[0], navCase);
1225: //ec.open();
1226: } else {
1227: ec
1228: .addPropertyChangeListener(new PropertyChangeListener() {
1229:
1230: public void propertyChange(
1231: PropertyChangeEvent evt) {
1232: if (EditorCookie.Observable.PROP_OPENED_PANES
1233: .equals(evt
1234: .getPropertyName())) {
1235: final JEditorPane[] panes = ec
1236: .getOpenedPanes();
1237: if (panes != null
1238: && panes.length > 0) {
1239: openPane(
1240: panes[0],
1241: navCase);
1242: }
1243: ec
1244: .removePropertyChangeListener(this );
1245: }
1246: }
1247: });
1248: ec.open();
1249: }
1250: }
1251: });
1252: }
1253: }
1254: }
1255: }
1256:
1257: private void openPane(JEditorPane pane, NavigationCase navCase) {
1258: final Cursor editCursor = pane.getCursor();
1259: pane.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
1260: pane.setCaretPosition(navCase.findPosition() + 2);
1261: pane.setCursor(editCursor);
1262: StatusDisplayer.getDefault().setStatusText(""); //NOI18N
1263: }
1264:
1265: /**
1266: * Moved this out of Page.java so that WebFolderListener also has an opportunity to
1267: * access the providers so that it can listen and decide wether or not to update
1268: * contents should be updated given a page.
1269: **/
1270: public static final Collection<? extends PageContentModelProvider> getPageContentModelProviders() {
1271: Lookup.Template<PageContentModelProvider> templ = new Lookup.Template<PageContentModelProvider>(
1272: PageContentModelProvider.class);
1273: final Lookup.Result<PageContentModelProvider> result = Lookup
1274: .getDefault().lookup(templ);
1275: Collection<? extends PageContentModelProvider> impls = result
1276: .allInstances();
1277: return impls;
1278: }
1279:
1280: static class TestAccessor {
1281:
1282: static Collection<String> getPagesInFacesConfig(
1283: final PageFlowController controller) {
1284: Set<NavigationRule> rules = TestAccessor
1285: .getAllNavigationRules(controller);
1286: return controller.getFacesConfigPageNames(rules);
1287: }
1288:
1289: static Collection<FileObject> getAllRelevantFiles(
1290: PageFlowController controller) {
1291: return controller.getAllProjectRelevantFilesObjects();
1292: }
1293:
1294: static Set<NavigationRule> getAllNavigationRules(
1295: PageFlowController controller) {
1296: return controller.navRule2String.keySet();
1297: }
1298:
1299: static Set<NavigationCase> getAllNavigationCases(
1300: PageFlowController controller) {
1301: return controller.navCase2NavCaseEdge.keySet();
1302: }
1303: }
1304: }
|