package runjettyrun.tabs; import java.io.File; import java.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IWorkspace; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IStatus; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfigurationWorkingCopy; import org.eclipse.jdt.debug.ui.launchConfigurations.JavaClasspathTab; import org.eclipse.jdt.debug.ui.launchConfigurations.JavaLaunchTab; import org.eclipse.jdt.launching.IJavaLaunchConfigurationConstants; import org.eclipse.jdt.launching.IRuntimeClasspathEntry; import org.eclipse.jdt.launching.JavaRuntime; import org.eclipse.jface.viewers.CheckStateChangedEvent; import org.eclipse.jface.viewers.ICheckStateListener; import org.eclipse.jface.viewers.ICheckStateProvider; import org.eclipse.jface.viewers.ITreeContentProvider; import org.eclipse.swt.SWT; import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; import runjettyrun.JettyLaunchConfigurationClassPathProvider; import runjettyrun.Plugin; import runjettyrun.RunJettyRunMessages; import runjettyrun.preferences.PreferenceConstants; import runjettyrun.tabs.action.AddClassFolderAction; import runjettyrun.tabs.action.AddExternalFolderAction; import runjettyrun.tabs.action.AddExternalJarAction; import runjettyrun.tabs.action.AddJarAction; import runjettyrun.tabs.action.AddProjectAction; import runjettyrun.tabs.action.RemoveAction; import runjettyrun.tabs.action.RestoreDefaultEntriesAction; import runjettyrun.tabs.action.RestoreDefaultSelectionAction; import runjettyrun.tabs.action.RuntimeClasspathAction; import runjettyrun.tabs.classpath.ClasspathEntry; import runjettyrun.tabs.classpath.ClasspathGroup; import runjettyrun.tabs.classpath.ClasspathLabelProvider; import runjettyrun.tabs.classpath.IClasspathViewer; import runjettyrun.tabs.classpath.IEntriesChangedListener; import runjettyrun.tabs.classpath.IRJRClasspathEntry; import runjettyrun.tabs.classpath.RuntimeClasspathViewer; import runjettyrun.tabs.classpath.UserClassesClasspathContentProvider; import runjettyrun.tabs.classpath.UserClassesClasspathModel; import runjettyrun.utils.ResourceUtil; /** * A launch configuration tab that displays and edits the user and bootstrap * classes comprising the classpath launch configuration attribute. * <p> * This class may be instantiated. * </p> * * @since 2.0 * @noextend This class is not intended to be subclassed by clients. */ public abstract class AbstractClasspathTab extends JavaLaunchTab implements IEntriesChangedListener { /** * Logger for this class */ private static final Logger logger = Logger .getLogger(AbstractClasspathTab.class.getName()); protected JettyLaunchConfigurationClassPathProvider classpathProvider = new JettyLaunchConfigurationClassPathProvider(); private String tabname; private String id; protected RuntimeClasspathViewer fClasspathViewer; private UserClassesClasspathModel fModel; private String message = null; private boolean activated = false; /** * If the items is not checked , the value string will be "1" , or it will be "0" or not contained in the key. */ protected Map<String,String> nonchecked; protected static final String DIALOG_SETTINGS_PREFIX = "JavaClasspathTab"; //$NON-NLS-1$ /** * The last launch config this tab was initialized from */ protected ILaunchConfiguration fLaunchConfiguration; public AbstractClasspathTab(String id, String tabname) { this.id = id; this.tabname = tabname; } public String getHeader(){ return tabname; } abstract String getCustomAttributeName(); abstract String getNonCheckedAttributeName(); protected JettyLaunchConfigurationClassPathProvider getClasspathProvider() { return classpathProvider; } /* * (non-Javadoc) * * @see org.eclipse.jdt.internal.debug.ui.launcher.IEntriesChangedListener# * entriesChanged * (org.eclipse.jdt.internal.debug.ui.launcher.IClasspathViewer) */ public void entriesChanged(IClasspathViewer viewer) { // setDirty(true); try { saveEntries(fLaunchConfiguration.getWorkingCopy()); } catch (CoreException e) { throw new IllegalStateException(e); } // updateLaunchConfigurationDialog(); } public void createHeaderControl(Composite parent){ } /* * (non-Javadoc) * * @see * org.eclipse.debug.ui.ILaunchConfigurationTab#createControl(org.eclipse * .swt.widgets.Composite) */ public void createControl(Composite parent) { Font font = parent.getFont(); Composite comp = new Composite(parent, SWT.NONE); setControl(comp); // PlatformUI.getWorkbench().getHelpSystem().setHelp(getControl(), // IJavaDebugHelpContextIds.LAUNCH_CONFIGURATION_DIALOG_CLASSPATH_TAB); GridLayout topLayout = new GridLayout(); topLayout.numColumns = 4; comp.setLayout(topLayout); GridData gd; Label label = new Label(comp, SWT.NONE); label.setText(getHeader()); gd = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING); gd.horizontalSpan = 4; label.setLayoutData(gd); createHeaderControl(comp); fClasspathViewer = new RuntimeClasspathViewer(comp); fClasspathViewer.setCheckStateProvider(new ICheckStateProvider() { public boolean isGrayed(Object element) { return ((IRJRClasspathEntry)element).isDefaultGrayed(); } public boolean isChecked(Object element) { boolean lazy = Plugin.getDefault().getPreferenceStore(). getBoolean(PreferenceConstants.P_LAZY_CLASSPATH_ENTRY_STATUS); if((!lazy) || activated){ //this operation is expensive, thus we only perform it after current tab has been activated at least once. return AbstractClasspathTab.this.isChecked((IRJRClasspathEntry)element); }else{ return false; } } }); fClasspathViewer.addCheckStateListener(new ICheckStateListener() { public void checkStateChanged(CheckStateChangedEvent event) { if (event.getChecked()) { checkEntry(nonchecked, (IRJRClasspathEntry) event.getElement()); } else { uncheckEntry(nonchecked, (IRJRClasspathEntry) event.getElement()); } try { ILaunchConfigurationWorkingCopy workingcopy = getWorkingCopy(); workingcopy.setAttribute(getNonCheckedAttributeName(), nonchecked); //Just in case, I am afraid this happened in some special case. //Anyway , if a map contains a null key will cause RunConfiguration get NPE when writing to XML. if(nonchecked.containsKey(null)){ nonchecked.remove(null); } workingcopy.doSave(); fClasspathViewer.refresh(event.getElement()); } catch (CoreException e) { logger.severe("CheckStateChangedEvent - exception: " + e); e.printStackTrace(); } } }); fClasspathViewer.addEntriesChangedListener(this); fClasspathViewer.getControl().setFont(font); fClasspathViewer.setLabelProvider(new ClasspathLabelProvider()); fClasspathViewer .setContentProvider(new UserClassesClasspathContentProvider( this)); Composite pathButtonComp = new Composite(comp, SWT.NONE); GridLayout pathButtonLayout = new GridLayout(); pathButtonLayout.marginHeight = 0; pathButtonLayout.marginWidth = 0; pathButtonComp.setLayout(pathButtonLayout); gd = new GridData(GridData.VERTICAL_ALIGN_BEGINNING | GridData.HORIZONTAL_ALIGN_FILL); pathButtonComp.setLayoutData(gd); pathButtonComp.setFont(font); createPathButtons(pathButtonComp); } protected boolean isChecked(IRJRClasspathEntry element){ if(element == null){ throw new IllegalArgumentException("Classpath Entry shouldn't be null."); } if(element instanceof ClasspathGroup){ return false; } String elementKey = element.getKey(); if( elementKey == null){ throw new IllegalStateException("elementKey shouldn't be null;"); } if(!nonchecked.containsKey(elementKey)){ // we don't have to lookup resource if item exist //2012/02/20 TonyQ //Note: the way we handle workspace relative path is to convert them to absolute path, // that might not be a good idea,but it simply the issue in first step at least. // // The difficult part is that when we get folder after classpath resolving , // that's usually in absolute path , and it's hard to find the original resource. // // The only risk to use absolute path is , // user will lost the uncheck settings when user change workspace/project path. // // Let's think about this later if any user complain about this. // File file = ResourceUtil.lookingFileFromPathString(element.getKey()); if(file.exists()){ elementKey = file.getAbsolutePath(); } } if(!nonchecked.containsKey(elementKey)){ return isDefaultChecked(element); } return nonchecked.get(elementKey).equals("0"); } protected boolean isDefaultChecked(IRJRClasspathEntry element){ if(element == null){ throw new IllegalArgumentException("Classpath Entry shouldn't be null."); } return element.isDefaultChecked(); } private ILaunchConfigurationWorkingCopy getWorkingCopy() throws CoreException{ ILaunchConfigurationWorkingCopy workingcopy = null; if( fLaunchConfiguration instanceof ILaunchConfigurationWorkingCopy){ workingcopy = (ILaunchConfigurationWorkingCopy) fLaunchConfiguration; }else{ workingcopy = fLaunchConfiguration.getWorkingCopy(); } return workingcopy; } private void checkEntry(Map<String,String> nonChecked, IRJRClasspathEntry entry) { if(entry.getKey() != null){ //for container , the realpath might be null , we just ignore it. nonChecked.put(entry.getKey(),"0"); } if (logger.isLoggable(Level.CONFIG)) { logger.config("Set<String>, IClasspathEntry - removed:" + entry.getKey()); } IRJRClasspathEntry[] entrys = entry.getEntries(); if (entrys != null && entrys.length > 0) { for (IRJRClasspathEntry childentry : entrys) { checkEntry(nonChecked, childentry); } } if (entry instanceof ClasspathEntry) { ClasspathEntry ce = (ClasspathEntry) entry; ITreeContentProvider contentProvider = (ITreeContentProvider) fClasspathViewer .getContentProvider(); IRJRClasspathEntry[] childentrys = (IRJRClasspathEntry[]) contentProvider .getChildren(ce); if (childentrys != null && childentrys.length > 0) { for (IRJRClasspathEntry childentry : childentrys) { checkEntry(nonChecked, childentry); } } } } private void uncheckEntry(Map<String,String> set, IRJRClasspathEntry entry) { String key = entry.getKey(); if(key != null){ //for container , the realpath might be null , we just ignore it. set.put(key,"1"); } if (logger.isLoggable(Level.CONFIG)) { logger.config("Set<String>, IClasspathEntry - add:" + entry.getRealPath()); } IRJRClasspathEntry[] entrys = entry.getEntries(); if (entrys != null && entrys.length > 0) { for (IRJRClasspathEntry childentry : entrys) { uncheckEntry(set, childentry); } } if (entry instanceof ClasspathEntry) { ClasspathEntry ce = (ClasspathEntry) entry; ITreeContentProvider contentProvider = (ITreeContentProvider) fClasspathViewer .getContentProvider(); IRJRClasspathEntry[] childentrys = (IRJRClasspathEntry[]) contentProvider .getChildren(ce); if (childentrys != null && childentrys.length > 0) { for (IRJRClasspathEntry childentry : childentrys) { uncheckEntry(set, childentry); } } } } /** * Creates the buttons to manipulate the classpath. * * @param pathButtonComp * composite buttons are contained in * @since 3.0 */ protected void createPathButtons(Composite pathButtonComp) { createButton(pathButtonComp, new RemoveAction(fClasspathViewer)); createButton(pathButtonComp, new AddProjectAction(fClasspathViewer)); createButton(pathButtonComp, new AddClassFolderAction(fClasspathViewer)); createButton(pathButtonComp, new AddJarAction(fClasspathViewer)); createButton(pathButtonComp, new AddExternalJarAction(fClasspathViewer, DIALOG_SETTINGS_PREFIX)); createButton(pathButtonComp, new AddExternalFolderAction( fClasspathViewer, DIALOG_SETTINGS_PREFIX)); RuntimeClasspathAction restoreSelectionAction = new RestoreDefaultSelectionAction( fClasspathViewer, this, this.getNonCheckedAttributeName()); createButton(pathButtonComp, restoreSelectionAction); restoreSelectionAction.setEnabled(true); RuntimeClasspathAction action = new RestoreDefaultEntriesAction( fClasspathViewer, this, this.getCustomAttributeName()); createButton(pathButtonComp, action); action.setEnabled(true); } /** * Creates a button for the given action. * * @param pathButtonComp * parent composite for the button * @param action * the action triggered by the button * @return the button that was created */ protected Button createButton(Composite pathButtonComp, RuntimeClasspathAction action) { Button button = createPushButton(pathButtonComp, action.getText(), null); action.setButton(button); return button; } /* * (non-Javadoc) * * @see * org.eclipse.debug.ui.ILaunchConfigurationTab#setDefaults(org.eclipse. * debug.core.ILaunchConfigurationWorkingCopy) */ public void setDefaults(ILaunchConfigurationWorkingCopy configuration) { } /* * (non-Javadoc) * * @see * org.eclipse.debug.ui.ILaunchConfigurationTab#initializeFrom(org.eclipse * .debug.core.ILaunchConfiguration) */ public void initializeFrom(ILaunchConfiguration configuration) { refresh(configuration); fClasspathViewer.expandToLevel(2); } /* * (non-Javadoc) * * @see * org.eclipse.debug.ui.ILaunchConfigurationTab#activated(org.eclipse.debug * .core.ILaunchConfigurationWorkingCopy) */ public void activated(ILaunchConfigurationWorkingCopy workingCopy) { try { activated = true; boolean useDefault = workingCopy.getAttribute( IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true); if (useDefault) { if (!isDefaultClasspath(getCurrentClasspath(), workingCopy)) { initializeFrom(workingCopy); return; } } fClasspathViewer.refresh(); } catch (CoreException e) { } } /** * Refreshes the classpath entries based on the current state of the given * launch configuration. */ private void refresh(ILaunchConfiguration configuration) { setLaunchConfiguration(configuration); try { fModel = createClasspathModel(configuration); } catch (Exception e) { message = e.getMessage(); setErrorMessage(e.getMessage()); } fClasspathViewer.setLaunchConfiguration(configuration); fClasspathViewer.setInput(fModel); setDirty(false); } public abstract UserClassesClasspathModel createClasspathModel( ILaunchConfiguration configuration) throws Exception; /* * (non-Javadoc) * * @see * org.eclipse.debug.ui.ILaunchConfigurationTab#performApply(org.eclipse * .debug.core.ILaunchConfigurationWorkingCopy) */ public void performApply(ILaunchConfigurationWorkingCopy configuration) { if (isDirty()) { saveEntries(configuration); } } private void saveEntries(ILaunchConfigurationWorkingCopy configuration) { configuration.setAttribute( IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true); IRuntimeClasspathEntry[] customClasspath = getCurrentCustomClasspath(); try { List<String> mementos = new ArrayList<String>( customClasspath.length); for (int i = 0; i < customClasspath.length; i++) { IRuntimeClasspathEntry entry = customClasspath[i]; mementos.add(entry.getMemento()); } configuration.setAttribute(getCustomAttributeName(), mementos); configuration.doSave(); } catch (CoreException e) { Plugin.statusDialog( RunJettyRunMessages.JavaClasspathTab_Unable_to_save_classpath_1, e.getStatus()); } } /** * Returns the classpath entries currently specified by this tab. * * @return the classpath entries currently specified by this tab */ private IRuntimeClasspathEntry[] getCurrentClasspath() { IRJRClasspathEntry[] user = fModel .getEntries(UserClassesClasspathModel.USER); List<IRuntimeClasspathEntry> entries = new ArrayList<IRuntimeClasspathEntry>( user.length); IRuntimeClasspathEntry entry; IRJRClasspathEntry userEntry; for (int i = 0; i < user.length; i++) { userEntry = user[i]; entry = null; if (userEntry instanceof ClasspathEntry) { entry = ((ClasspathEntry) userEntry).getDelegate(); } else if (userEntry instanceof IRuntimeClasspathEntry) { entry = (IRuntimeClasspathEntry) user[i]; } if (entry != null) { entry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES); entries.add(entry); } } return (IRuntimeClasspathEntry[]) entries .toArray(new IRuntimeClasspathEntry[entries.size()]); } private IRuntimeClasspathEntry[] getCurrentCustomClasspath() { IRJRClasspathEntry[] user = fModel .getEntries(UserClassesClasspathModel.CUSTOM); List<IRuntimeClasspathEntry> entries = new ArrayList<IRuntimeClasspathEntry>( user.length); IRuntimeClasspathEntry entry; IRJRClasspathEntry userEntry; for (int i = 0; i < user.length; i++) { userEntry = user[i]; entry = null; if (userEntry instanceof ClasspathEntry) { entry = ((ClasspathEntry) userEntry).getDelegate(); } else if (userEntry instanceof IRuntimeClasspathEntry) { entry = (IRuntimeClasspathEntry) user[i]; } if (entry != null) { entry.setClasspathProperty(IRuntimeClasspathEntry.USER_CLASSES); entries.add(entry); } } return (IRuntimeClasspathEntry[]) entries .toArray(new IRuntimeClasspathEntry[entries.size()]); } /** * Returns whether the specified classpath is equivalent to the default * classpath for this configuration. * * @param classpath * classpath to compare to default * @param configuration * original configuration * @return whether the specified classpath is equivalent to the default * classpath for this configuration */ private boolean isDefaultClasspath(IRuntimeClasspathEntry[] classpath, ILaunchConfiguration configuration) { try { ILaunchConfigurationWorkingCopy wc = configuration.getWorkingCopy(); wc.setAttribute( IJavaLaunchConfigurationConstants.ATTR_DEFAULT_CLASSPATH, true); IRuntimeClasspathEntry[] entries = JavaRuntime .computeUnresolvedRuntimeClasspath(wc); if (classpath.length == entries.length) { for (int i = 0; i < entries.length; i++) { IRuntimeClasspathEntry entry = entries[i]; if (!entry.equals(classpath[i])) { return false; } } return true; } return false; } catch (CoreException e) { return false; } } /* * (non-Javadoc) * * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getName() */ public String getName() { return this.tabname; } /** * @see org.eclipse.debug.ui.AbstractLaunchConfigurationTab#getId() * * @since 3.3 */ public String getId() { return "runjettyrun.tabs.classpath." + this.id; //$NON-NLS-1$ } /** * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() */ public static Image getClasspathImage() { return JavaClasspathTab.getClasspathImage(); } /** * Sets the launch configuration for this classpath tab */ @SuppressWarnings("unchecked") private void setLaunchConfiguration(ILaunchConfiguration config) { fLaunchConfiguration = config; try { nonchecked = (Map<String,String>) fLaunchConfiguration.getAttribute(getNonCheckedAttributeName(),(Map<String,String>)null); } catch (CoreException e) { } if(nonchecked == null){ nonchecked = new HashMap<String,String>(); } } /** * Returns the current launch configuration */ public ILaunchConfiguration getLaunchConfiguration() { return fLaunchConfiguration; } /* * (non-Javadoc) * * @see org.eclipse.debug.ui.ILaunchConfigurationTab#dispose() */ public void dispose() { if (fClasspathViewer != null) { fClasspathViewer.removeEntriesChangedListener(this); } super.dispose(); } /* * (non-Javadoc) * * @see org.eclipse.debug.ui.ILaunchConfigurationTab#getImage() */ public Image getImage() { return getClasspathImage(); } /* * (non-Javadoc) * * @see * org.eclipse.debug.ui.ILaunchConfigurationTab#isValid(org.eclipse.debug * .core.ILaunchConfiguration) */ public boolean isValid(ILaunchConfiguration launchConfig) { if(fModel == null){ setErrorMessage("RunJettyRun can't read the "+tabname+" data , reason:"+message ); return false; } setErrorMessage(null); setMessage(null); String projectName = null; try { projectName = launchConfig.getAttribute( IJavaLaunchConfigurationConstants.ATTR_PROJECT_NAME, ""); //$NON-NLS-1$ } catch (CoreException e) { return false; } String checkExist = fModel.checkIfCustomEntriesExisting(); if(checkExist != null){ setErrorMessage(checkExist); } if (projectName.length() > 0) { IWorkspace workspace = ResourcesPlugin.getWorkspace(); IStatus status = workspace.validateName(projectName, IResource.PROJECT); if (status.isOK()) { IProject project = ResourcesPlugin.getWorkspace().getRoot() .getProject(projectName); if (!project.exists()) { setErrorMessage(MessageFormat.format( RunJettyRunMessages.ClasspathTab_projectNotFound, new Object[] { projectName })); return false; } if (!project.isOpen()) { setErrorMessage(MessageFormat.format( RunJettyRunMessages.JavaMainTab_21, new Object[] { projectName })); return false; } } else { setErrorMessage(MessageFormat.format( RunJettyRunMessages.JavaMainTab_19, new Object[] { status.getMessage() })); return false; } } IRuntimeClasspathEntry[] entries = fModel.getAllEntries(); int type = -1; for (int i = 0; i < entries.length; i++) { type = entries[i].getType(); if (type == IRuntimeClasspathEntry.ARCHIVE) { if (!entries[i].getPath().isAbsolute()) { setErrorMessage(MessageFormat .format(RunJettyRunMessages.JavaClasspathTab_Invalid_runtime_classpath_1, new Object[] { entries[i].getPath() .toString() })); return false; } } if (type == IRuntimeClasspathEntry.PROJECT) { IResource res = entries[i].getResource(); if (res != null && !res.isAccessible()) { setErrorMessage(MessageFormat.format( RunJettyRunMessages.JavaClasspathTab_1, new Object[] { res.getName() })); return false; } } } return true; } /** * Returns whether the bootpath should be displayed. * * @return whether the bootpath should be displayed * @since 3.0 */ public boolean isShowBootpath() { return true; } @SuppressWarnings("unchecked") public void refreshSelection(){ try { nonchecked = (Map<String,String>) fLaunchConfiguration.getAttribute(getNonCheckedAttributeName(),(Map<String,String>)null); } catch (CoreException e) { } if(nonchecked == null){ nonchecked = new HashMap<String,String>(); } this.fClasspathViewer.refresh(); } /** * @return Returns the classpath model. */ protected UserClassesClasspathModel getModel() { return fModel; } }