| author | Steffen Pingel | 2012-02-25 09:07:54 (EST) |
|---|---|---|
| committer | Steffen Pingel | 2012-02-25 09:07:54 (EST) |
| commit | 3910a9fb5ac20c523859ca17ef8aac80d6c17245 (patch) (side-by-side diff) | |
| tree | e4653a3c4cbeacb151c30d7bbe0992c085546535 | |
| parent | c04f8f262fa49407fd6dff4420f4644ec8470fdd (diff) | |
| download | org.eclipse.mylyn.commons-3910a9fb5ac20c523859ca17ef8aac80d6c17245.zip org.eclipse.mylyn.commons-3910a9fb5ac20c523859ca17ef8aac80d6c17245.tar.gz org.eclipse.mylyn.commons-3910a9fb5ac20c523859ca17ef8aac80d6c17245.tar.bz2 | |
NEW - bug 371599: Workbench fails to start: Plug-in
org.eclipse.mylyn.tasks.ui was unable to load class
org.eclipse.mylyn.internal.tasks.ui.TaskTrimWidget
https://bugs.eclipse.org/bugs/show_bug.cgi?id=371599
Change-Id: Ia209c264ad178e8b7397f3e7354e48a30bdc9b80
93 files changed, 7371 insertions, 12 deletions
diff --git a/stubs/org.eclipse.mylyn.commons.identity/.project b/stubs/org.eclipse.mylyn.commons.identity/.project index 240751e..5ff8cc4 100644 --- a/stubs/org.eclipse.mylyn.commons.identity/.project +++ b/stubs/org.eclipse.mylyn.commons.identity/.project @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <projectDescription> - <name>org.eclipse.mylyn.commons.identity</name> + <name>org.eclipse.mylyn.commons.identity-stub</name> <comment></comment> <projects> </projects> diff --git a/stubs/org.eclipse.mylyn.commons.identity/META-INF/MANIFEST.MF b/stubs/org.eclipse.mylyn.commons.identity/META-INF/MANIFEST.MF index 415523c..3680286 100644 --- a/stubs/org.eclipse.mylyn.commons.identity/META-INF/MANIFEST.MF +++ b/stubs/org.eclipse.mylyn.commons.identity/META-INF/MANIFEST.MF @@ -2,8 +2,14 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.mylyn.commons.identity;singleton:=true -Bundle-Version: 0.9.0.v20111206-0100 +Bundle-Version: 0.9.0.v20120225-0100 Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ClassPath: . Bundle-Localization: plugin +Require-Bundle: org.eclipse.core.runtime +Bundle-ActivationPolicy: lazy +Export-Package: org.eclipse.mylyn.commons.identity;x-internal:=true, + org.eclipse.mylyn.commons.identity.spi;x-internal:=true, + org.eclipse.mylyn.internal.commons.identity;x-internal:=true, + org.eclipse.mylyn.internal.commons.identity.gravatar;x-internal:=true diff --git a/stubs/org.eclipse.mylyn.commons.identity/pom.xml b/stubs/org.eclipse.mylyn.commons.identity/pom.xml index c67fb0b..5e8a41b 100644 --- a/stubs/org.eclipse.mylyn.commons.identity/pom.xml +++ b/stubs/org.eclipse.mylyn.commons.identity/pom.xml @@ -8,7 +8,7 @@ <version>3.7.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.mylyn.commons.identity</artifactId> - <version>0.9.0.v20111206-0100</version> + <version>0.9.0.v20120225-0100</version> <packaging>eclipse-plugin</packaging> <build> <plugins> diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/.placeholder b/stubs/org.eclipse.mylyn.commons.identity/src/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/stubs/org.eclipse.mylyn.commons.identity/src/.placeholder +++ b/dev/null diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/Account.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/Account.java new file mode 100644 index 0000000..e3b64ea --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/Account.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.identity; + +import java.io.Serializable; + +import org.eclipse.core.runtime.Assert; + +/** + * @author Steffen Pingel + */ +public class Account implements Serializable { + + private static final long serialVersionUID = 3670630150657553390L; + + public static Account id(String id) { + Assert.isNotNull(id); + return new Account(id); + } + + private final String id; + + private String kind; + + private String name; + + private String url; + + private Account(String id) { + this.id = id; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + Account other = (Account) obj; + if (id == null) { + if (other.id != null) { + return false; + } + } else if (!id.equals(other.id)) { + return false; + } + if (kind == null) { + if (other.kind != null) { + return false; + } + } else if (!kind.equals(other.kind)) { + return false; + } + if (name == null) { + if (other.name != null) { + return false; + } + } else if (!name.equals(other.name)) { + return false; + } + if (url == null) { + if (other.url != null) { + return false; + } + } else if (!url.equals(other.url)) { + return false; + } + return true; + } + + public String getId() { + return id; + } + + public String getKind() { + return kind; + } + + public String getName() { + return name; + } + + public String getUrl() { + return url; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((id == null) ? 0 : id.hashCode()); + result = prime * result + ((kind == null) ? 0 : kind.hashCode()); + result = prime * result + ((name == null) ? 0 : name.hashCode()); + result = prime * result + ((url == null) ? 0 : url.hashCode()); + return result; + } + + public Account kind(String kind) { + this.kind = kind; + return this; + } + + public Account name(String name) { + this.name = name; + return this; + } + + public Account url(String url) { + this.url = url; + return this; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IIdentity.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IIdentity.java new file mode 100644 index 0000000..0e1efff --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IIdentity.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.identity; + +import java.beans.PropertyChangeListener; +import java.util.UUID; +import java.util.concurrent.Future; + +/** + * @author Steffen Pingel + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @since 0.8 + */ +public interface IIdentity { + + // public static final String KIND_DEFAULT = "org.eclipse.mylyn.commons.identity.default"; //$NON-NLS-1$ + // + // public static final String KIND_EMAIL = "org.eclipse.mylyn.commons.identity.email"; //$NON-NLS-1$ + + public void addAccount(Account account); + + public void addPropertyChangeListener(PropertyChangeListener listener); + + public Account getAccountById(String id); + + public Account getAccountByKind(String kind); + + public Account[] getAccounts(); + + public String[] getAliases(); + + public UUID getId(); + + public void removeAccount(Account account); + + public void removePropertyChangeListener(PropertyChangeListener listener); + + public Future<IProfileImage> requestImage(int preferredWidth, int preferredHeight); + + public Future<IProfile> requestProfile(); + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IProfile.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IProfile.java new file mode 100644 index 0000000..42c996e --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IProfile.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.identity; + +/** + * @author Steffen Pingel + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @since 0.8 + */ +public interface IProfile { + + public abstract String getCity(); + + public abstract String getCountry(); + + public abstract String getEmail(); + + public abstract IIdentity getIdentity(); + + public abstract String getName(); + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IProfileImage.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IProfileImage.java new file mode 100644 index 0000000..763fbc0 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/IProfileImage.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.identity; + +/** + * @author Steffen Pingel + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + * @since 0.8 + */ +public interface IProfileImage { + + public abstract byte[] getData(); + + public abstract int getWidth(); + + public abstract int getHeight(); + + public abstract String getFormat(); + +}
\ No newline at end of file diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/IdentityConnector.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/IdentityConnector.java new file mode 100644 index 0000000..ca0f84b --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/IdentityConnector.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.identity.spi; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.mylyn.commons.identity.IIdentity; + +/** + * @author Steffen Pingel + * @since 0.8 + */ +public abstract class IdentityConnector { + + public abstract ProfileImage getImage(IIdentity identity, int preferredWidth, int preferredHeight, + IProgressMonitor monitor) throws CoreException; + + public abstract boolean supportsImageSize(int preferredWidth, int preferredHeight); + + public abstract void updateProfile(Profile profile, IProgressMonitor monitor) throws CoreException; + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/Profile.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/Profile.java new file mode 100644 index 0000000..6183a6f --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/Profile.java @@ -0,0 +1,87 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.identity.spi; + +import java.io.Serializable; + +import org.eclipse.mylyn.commons.identity.IIdentity; +import org.eclipse.mylyn.commons.identity.IProfile; + +/** + * @author Steffen Pingel + * @since 0.8 + */ +public final class Profile implements IProfile, Serializable { + + private static final long serialVersionUID = -1079729573911113939L; + + private String city; + + private String country; + + private String email; + + private final IIdentity identity; + + private String name; + + private int timeZoneOffset; + + public Profile(IIdentity identity) { + this.identity = identity; + } + + public String getCity() { + return city; + } + + public String getCountry() { + return country; + } + + public String getEmail() { + return email; + } + + public IIdentity getIdentity() { + return identity; + } + + public String getName() { + return name; + } + + public int getTimeZoneOffset() { + return timeZoneOffset; + } + + public void setCity(String city) { + this.city = city; + } + + public void setCountry(String country) { + this.country = country; + } + + public void setEmail(String email) { + this.email = email; + } + + public void setName(String name) { + this.name = name; + } + + public void setTimeZoneOffset(int timeZoneOffset) { + this.timeZoneOffset = timeZoneOffset; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/ProfileImage.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/ProfileImage.java new file mode 100644 index 0000000..ad047bc --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/commons/identity/spi/ProfileImage.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.identity.spi; + +import java.io.Serializable; + +import org.eclipse.mylyn.commons.identity.IProfileImage; + +/** + * @author Steffen Pingel + * @since 0.8 + */ +public final class ProfileImage implements IProfileImage, Serializable { + + private static final long serialVersionUID = 8211724823497362719L; + + byte[] data; + + int width; + + int height; + + String format; + + long timestamp; + + public ProfileImage(byte[] data, int width, int height, String format) { + this.data = data; + this.width = width; + this.height = height; + this.format = format; + this.timestamp = System.currentTimeMillis(); + } + + public byte[] getData() { + return data; + } + + public int getWidth() { + return width; + } + + public int getHeight() { + return height; + } + + public String getFormat() { + return format; + } + + public long getTimestamp() { + return timestamp; + } + + public void setTimestamp(long timestamp) { + this.timestamp = timestamp; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/Identity.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/Identity.java new file mode 100644 index 0000000..ddc5eac --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/Identity.java @@ -0,0 +1,311 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.concurrent.CopyOnWriteArraySet; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.mylyn.commons.identity.Account; +import org.eclipse.mylyn.commons.identity.IIdentity; +import org.eclipse.mylyn.commons.identity.IProfile; +import org.eclipse.mylyn.commons.identity.IProfileImage; +import org.eclipse.mylyn.commons.identity.spi.Profile; +import org.eclipse.mylyn.commons.identity.spi.ProfileImage; + +/** + * @author Steffen Pingel + */ +public class Identity implements IIdentity { + + private static abstract class FutureJob<T> extends Job implements Future<T> { + + private boolean cancelled; + + private final AtomicReference<Throwable> futureException = new AtomicReference<Throwable>(); + + private final AtomicReference<T> futureResult = new AtomicReference<T>(); + + private final CountDownLatch resultLatch = new CountDownLatch(1); + + public FutureJob(String name) { + super(name); + } + + public boolean cancel(boolean mayInterruptIfRunning) { + this.cancelled = true; + return this.cancel(); + } + + public T get() throws InterruptedException, ExecutionException { + resultLatch.await(); + return getFutureResult(); + } + + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + if (!resultLatch.await(timeout, unit)) { + throw new TimeoutException(); + } + return getFutureResult(); + } + + public boolean isCancelled() { + return this.cancelled; + } + + public boolean isDone() { + return getResult() != null; + } + + private T getFutureResult() throws ExecutionException { + Throwable t = futureException.get(); + if (t != null) { + throw new ExecutionException(t); + } + return futureResult.get(); + } + + protected void done() { + if (resultLatch.getCount() > 0) { + error(new RuntimeException()); + } + resultLatch.countDown(); + } + + protected IStatus error(Throwable t) { + futureException.set(t); + resultLatch.countDown(); + if (t instanceof OperationCanceledException) { + return Status.CANCEL_STATUS; + } + return Status.OK_STATUS; + } + + protected IStatus success(T result) { + futureResult.set(result); + resultLatch.countDown(); + return Status.OK_STATUS; + } + + } + + private static final class FutureResult<T> implements Future<T> { + + private final T result; + + private FutureResult(T result) { + this.result = result; + } + + public boolean cancel(boolean mayInterruptIfRunning) { + return true; + } + + public T get() throws InterruptedException, ExecutionException { + return result; + } + + public T get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException { + return result; + } + + public boolean isCancelled() { + return false; + } + + public boolean isDone() { + return true; + } + } + + private final Set<Account> accounts; + + private final UUID id; + + private List<ProfileImage> images; + + private final List<PropertyChangeListener> listeners; + + private final IdentityModel model; + + private Profile profile; + + private boolean refreshProfile; + + public Identity(IdentityModel model) { + this.model = model; + this.id = UUID.randomUUID(); + this.accounts = new CopyOnWriteArraySet<Account>(); + this.listeners = new CopyOnWriteArrayList<PropertyChangeListener>(); + } + + public void addAccount(Account account) { + accounts.add(account); + refreshProfile = true; + } + + public void addPropertyChangeListener(PropertyChangeListener listener) { + listeners.add(listener); + } + + public Account[] getAccounts() { + return accounts.toArray(new Account[accounts.size()]); + } + + public Account getAccountById(String id) { + if (id == null) { + return null; + } + for (Account account : accounts) { + if (id.equals(account.getId())) { + return account; + } + } + return null; + } + + public Account getAccountByKind(String kind) { + if (kind == null) { + return null; + } + for (Account account : accounts) { + if (kind.equals(account.getKind())) { + return account; + } + } + return null; + } + + public String[] getAliases() { + Set<String> aliases = new HashSet<String>(accounts.size()); + for (Account account : accounts) { + aliases.add(account.getId()); + } + return aliases.toArray(new String[aliases.size()]); + } + + public UUID getId() { + return id; + } + + public boolean is(Account account) { + return accounts.contains(account); + } + + public boolean is(String id) { + return getAccountById(id) != null; + } + + public void removeAccount(Account account) { + accounts.remove(account); + } + + public void removePropertyChangeListener(PropertyChangeListener listener) { + listeners.remove(listener); + } + + public synchronized Future<IProfileImage> requestImage(final int preferredWidth, final int preferredHeight) { + if (images != null) { + for (final ProfileImage image : images) { + if (image.getWidth() == preferredWidth && image.getHeight() == preferredHeight) { + return new FutureResult<IProfileImage>(image); + } + } + } + FutureJob<IProfileImage> job = new FutureJob<IProfileImage>("Retrieving Image") { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + ProfileImage image = model.getImage(Identity.this, preferredWidth, preferredHeight, monitor); + if (image != null) { + addImage(image); + } + return success(image); + } catch (Throwable t) { + return error(t); + } finally { + done(); + } + } + }; + job.schedule(); + return job; + } + + public Future<IProfile> requestProfile() { + if (profile != null && !refreshProfile) { + return new FutureResult<IProfile>(profile); + } + + refreshProfile = false; + FutureJob<IProfile> job = new FutureJob<IProfile>("Retrieving Profile") { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + Profile profile = new Profile(Identity.this); + model.updateProfile(profile, monitor); + setProfile(profile); + return success(profile); + } catch (Throwable t) { + return error(t); + } finally { + done(); + } + } + }; + job.schedule(); + return job; + } + + private void firePropertyChangeEvent(String propertyName, Object oldValue, Object newValue) { + PropertyChangeEvent event = new PropertyChangeEvent(this, propertyName, oldValue, newValue); + for (PropertyChangeListener listener : listeners) { + listener.propertyChange(event); + } + } + + protected synchronized void addImage(ProfileImage image) { + if (images == null) { + images = new ArrayList<ProfileImage>(); + } + images.add(image); + firePropertyChangeEvent("image", null, image); //$NON-NLS-1$ + } + + protected synchronized void removeImage(ProfileImage image) { + if (images != null) { + images.remove(image); + } + } + + protected void setProfile(Profile profile) { + this.profile = profile; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/IdentityModel.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/IdentityModel.java new file mode 100644 index 0000000..189feea --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/IdentityModel.java @@ -0,0 +1,103 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity; + +import java.io.File; +import java.io.Serializable; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.WeakHashMap; +import java.util.concurrent.CopyOnWriteArrayList; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.mylyn.commons.identity.Account; +import org.eclipse.mylyn.commons.identity.IIdentity; +import org.eclipse.mylyn.commons.identity.spi.IdentityConnector; +import org.eclipse.mylyn.commons.identity.spi.Profile; +import org.eclipse.mylyn.commons.identity.spi.ProfileImage; +import org.eclipse.mylyn.internal.commons.identity.gravatar.GravatarConnector; + +/** + * @author Steffen Pingel + * @since 0.8 + */ +public final class IdentityModel implements Serializable { + + private transient final List<IdentityConnector> connectors; + + private transient final File cacheDirectory; + + private final Map<UUID, Identity> identityById; + + public IdentityModel(File cacheDirectory) { + this.cacheDirectory = cacheDirectory; + connectors = new CopyOnWriteArrayList<IdentityConnector>(); + identityById = new WeakHashMap<UUID, Identity>(); + } + + public void addConnector(IdentityConnector connector) { + connectors.add(new GravatarConnector()); + } + + public synchronized IIdentity getIdentity(Account account) { + for (Identity identity : identityById.values()) { + if (identity.is(account)) { + return identity; + } + } + + Identity identity = new Identity(this); + identity.addAccount(account); + + // cache identity + identityById.put(identity.getId(), identity); + + return identity; + } + + public void removeConnector(IdentityConnector connector) { + connectors.remove(new GravatarConnector()); + } + + public IIdentity[] getIdentities() { + return identityById.values().toArray(new IIdentity[identityById.values().size()]); + } + + public ProfileImage getImage(Identity identity, int preferredWidth, int preferredHeight, IProgressMonitor monitor) + throws CoreException { + for (IdentityConnector connector : connectors) { + ProfileImage image = connector.getImage(identity, preferredHeight, preferredHeight, monitor); + if (image != null) { + return image; + } + } + return null; + } + + public void updateProfile(Profile profile, IProgressMonitor monitor) throws CoreException { + Account[] accounts = profile.getIdentity().getAccounts(); + for (Account account : accounts) { + if (profile.getEmail() == null && account.getId().contains("@")) { //$NON-NLS-1$ + profile.setEmail(account.getId()); + } + if (profile.getName() == null && account.getName() != null) { + profile.setName(account.getName()); + } + } + for (IdentityConnector connector : connectors) { + connector.updateProfile(profile, monitor); + } + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/Gravatar.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/Gravatar.java new file mode 100644 index 0000000..183e261 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/Gravatar.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +import java.io.Serializable; + +import org.eclipse.core.runtime.Assert; + +/** + * Gravatar class containing id and image data. + * + * @author Kevin Sawicki (kevin@github.com) + */ +public class Gravatar implements Serializable { + + /** + * serialVersionUID + */ + private static final long serialVersionUID = 7303486086217698261L; + + private final String id; + + private final long updateTime; + + private final byte[] bytes; + + /** + * Create gravatar + * + * @param id + * @param updateTime + * @param bytes + */ + public Gravatar(String id, long updateTime, byte[] bytes) { + Assert.isNotNull(id, "Id cannot be null"); //$NON-NLS-1$ + Assert.isNotNull(bytes, "Bytes cannot be null"); //$NON-NLS-1$ + this.id = id; + this.updateTime = updateTime; + this.bytes = bytes; + } + + /** + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + return this.id.hashCode(); + } + + /** + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (obj == this) { + return true; + } else if (obj instanceof Gravatar) { + return getId().equals(((Gravatar) obj).getId()); + } + return false; + } + + /** + * Get gravatar image as byte array + * + * @return non-null byte array + */ + public byte[] getBytes() { + byte[] copy = new byte[this.bytes.length]; + System.arraycopy(this.bytes, 0, copy, 0, copy.length); + return copy; + } + + /** + * @see java.lang.Object#toString() + */ + @Override + public String toString() { + return this.id; + } + + /** + * Get gravatar id + * + * @return id + */ + public String getId() { + return this.id; + } + + /** + * Get time gravatar was loaded + * + * @return update time + */ + public long getUpdateTime() { + return this.updateTime; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarCallbackAdapter.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarCallbackAdapter.java new file mode 100644 index 0000000..a669898 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarCallbackAdapter.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +/** + * Base implementation of {@link IGravatarCallback} + * + * @author Kevin Sawicki (kevin@github.com) + */ +public abstract class GravatarCallbackAdapter implements IGravatarCallback { + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarCallback#loaded(org.eclipse.mylyn.internal.commons.identity.gravatar.Gravatar) + */ + public void loaded(Gravatar avatar) { + // Does nothing sub-clsases should override + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarCallback#error(java.lang.Exception) + */ + public void error(Exception exception) { + // Does nothing sub-clsases should override + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarConnector.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarConnector.java new file mode 100644 index 0000000..78d72bf --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarConnector.java @@ -0,0 +1,111 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylyn.commons.identity.Account; +import org.eclipse.mylyn.commons.identity.IIdentity; +import org.eclipse.mylyn.commons.identity.spi.IdentityConnector; +import org.eclipse.mylyn.commons.identity.spi.Profile; +import org.eclipse.mylyn.commons.identity.spi.ProfileImage; + +/** + * @author Steffen Pingel + */ +public class GravatarConnector extends IdentityConnector { + + public static final String KIND = "org.eclipse.mylyn.commons.identity.gravatar"; //$NON-NLS-1$ + + private final GravatarStore store; + + private final int DEFAULT_SIZE = 80; + + private final Map<String, Long> noImageHashByTimeStamp = new ConcurrentHashMap<String, Long>(); + + public GravatarConnector() { + this.store = new GravatarStore(); + } + + @Override + public ProfileImage getImage(IIdentity identity, int preferredWidth, int preferredHeight, IProgressMonitor monitor) + throws CoreException { + String id = getHash(identity); + if (id == null) { + return null; + } + + // avoid retrieving image again i + if (noImageHashByTimeStamp.containsKey(id)) { + return null; + } + + // store id for future retrieval + identity.addAccount(Account.id(id).kind(KIND)); + + int size = getSize(preferredWidth); + Gravatar gravatar; + try { + gravatar = store.loadGravatarByHash(id, size, null); + } catch (IOException e) { + throw new CoreException(new Status(IStatus.ERROR, KIND, e.getMessage(), e)); + } + + if (gravatar != null) { + return new ProfileImage(gravatar.getBytes(), size, size, "jpg"); //$NON-NLS-1$ + } else { + noImageHashByTimeStamp.put(id, Long.valueOf(System.currentTimeMillis())); + } + return null; + } + + private int getSize(int preferredSize) { + if (preferredSize < 1 && preferredSize > 512) { + return DEFAULT_SIZE; + } + return preferredSize; + } + + @Override + public boolean supportsImageSize(int preferredWidth, int preferredHeight) { + return (preferredWidth >= 1 && preferredWidth <= 512 && preferredHeight >= 1 && preferredHeight <= 512 && preferredWidth == preferredHeight); + } + + @Override + public void updateProfile(Profile profile, IProgressMonitor monitor) throws CoreException { + // TODO retrieve Gravatar profile information + } + + private String getHash(IIdentity identity) { + Account account = identity.getAccountByKind(KIND); + if (account != null) { + if (GravatarUtils.isValidHash(account.getId())) { + return account.getId(); + } + } + + for (String alias : identity.getAliases()) { + if (GravatarUtils.isValidEmail(alias)) { + return GravatarUtils.getHash(alias); + } + } + + return null; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarStore.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarStore.java new file mode 100644 index 0000000..79e535c --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarStore.java @@ -0,0 +1,283 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.net.HttpURLConnection; +import java.net.URL; +import java.text.MessageFormat; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; + +/** + * Class that loads and stores gravatars. + * + * @author Kevin Sawicki (kevin@github.com) + */ +public class GravatarStore implements Serializable, ISchedulingRule { + + private boolean cacheEnabled; + + /** + * TIMEOUT + */ + public static final int TIMEOUT = 30 * 1000; + + /** + * BUFFER_SIZE + */ + public static final int BUFFER_SIZE = 8192; + + public enum Rating { + G, PG, R, X + }; + + private static final long serialVersionUID = 6084425297832914970L; + + private long lastRefresh = 0L; + + private final String url; + + private Map<String, Gravatar> avatars; + + /** + * Create gravatar store + */ + public GravatarStore() { + this(IGravatarConstants.URL); + } + + /** + * Create gravatar store + * + * @param url + */ + public GravatarStore(String url) { + Assert.isNotNull(url, "Url cannot be null"); //$NON-NLS-1$ + // Ensure trailing slash + if (!url.endsWith("/")) { //$NON-NLS-1$ + url += "/"; //$NON-NLS-1$ + } + this.url = url; + } + + public boolean isCacheEnabled() { + return avatars != null; + } + + public void setCacheEnabled(boolean cacheEnabled) { + this.cacheEnabled = cacheEnabled; + if (cacheEnabled && avatars == null) { + avatars = Collections.synchronizedMap(new HashMap<String, Gravatar>()); + } else if (!cacheEnabled && avatars != null) { + avatars = null; + } + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#getRefreshTime() + */ + public long getRefreshTime() { + return this.lastRefresh; + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#containsGravatar(java.lang.String) + */ + public boolean containsGravatar(String hash) { + return hash != null && avatars != null ? this.avatars.containsKey(hash) : false; + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#scheduleRefresh() + */ + public GravatarStore scheduleRefresh() { + Job refresh = new Job(Messages.GravatarStore_RefreshJobName) { + + @Override + protected IStatus run(IProgressMonitor monitor) { + refresh(monitor); + return Status.OK_STATUS; + } + }; + refresh.setRule(this); + refresh.schedule(); + return this; + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#refresh(org.eclipse.core.runtime.IProgressMonitor) + */ + public GravatarStore refresh(IProgressMonitor monitor) { + if (this.avatars == null) { + return this; + } + if (monitor == null) { + monitor = new NullProgressMonitor(); + } + String[] entries = null; + synchronized (this.avatars) { + entries = new String[this.avatars.size()]; + entries = this.avatars.keySet().toArray(entries); + } + monitor.beginTask("", entries.length); //$NON-NLS-1$ + for (String entry : entries) { + if (monitor.isCanceled()) { + break; + } + monitor.setTaskName(MessageFormat.format(Messages.GravatarStore_LoadingAvatar, entry)); + try { + loadGravatarByHash(entry); + } catch (IOException ignore) { + } + monitor.worked(1); + } + monitor.done(); + this.lastRefresh = System.currentTimeMillis(); + return this; + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#loadGravatarByHash(java.lang.String, + * org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarCallback) + */ + public GravatarStore loadGravatarByHash(final String hash, final IGravatarCallback callback) { + String title = MessageFormat.format(Messages.GravatarStore_LoadingAvatar, hash); + Job job = new Job(title) { + + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + Gravatar avatar = loadGravatarByHash(hash); + if (avatar != null && callback != null) { + callback.loaded(avatar); + } + } catch (IOException e) { + if (callback != null) { + callback.error(e); + } + } + return Status.OK_STATUS; + } + }; + job.setRule(this); + job.schedule(); + return this; + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#loadGravatarByEmail(java.lang.String, + * org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarCallback) + */ + public GravatarStore loadGravatarByEmail(String email, IGravatarCallback callback) { + loadGravatarByHash(GravatarUtils.getHash(email), callback); + return this; + } + + public Gravatar loadGravatarByHash(String hash) throws IOException { + return loadGravatarByHash(hash, -1, null); + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#loadGravatarByHash(java.lang.String) + */ + public Gravatar loadGravatarByHash(String hash, int size, Rating rating) throws IOException { + Assert.isLegal(size == -1 || (size >= 1 && size <= 512), "size must have a value of -1 or between 1 and 512"); //$NON-NLS-1$ + if (!GravatarUtils.isValidHash(hash)) { + return null; + } + + Gravatar avatar = null; + String location = this.url + hash + "?d=404"; //$NON-NLS-1$ + if (size != -1) { + location += "&s=" + size; //$NON-NLS-1$ + } + if (rating != null) { + location += "&r=" + rating.name().toLowerCase(); //$NON-NLS-1$ + } + HttpURLConnection connection = (HttpURLConnection) new URL(location).openConnection(); + connection.setConnectTimeout(TIMEOUT); + connection.setUseCaches(false); + connection.connect(); + + if (connection.getResponseCode() != 200) { + return null; + } + + ByteArrayOutputStream output = new ByteArrayOutputStream(); + InputStream input = connection.getInputStream(); + try { + byte[] buffer = new byte[BUFFER_SIZE]; + int read = -1; + while ((read = input.read(buffer)) != -1) { + output.write(buffer, 0, read); + } + } finally { + try { + input.close(); + } catch (IOException ignore) { + } + } + avatar = new Gravatar(hash, System.currentTimeMillis(), output.toByteArray()); + if (this.avatars != null) { + this.avatars.put(hash, avatar); + } + return avatar; + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#loadGravatarByEmail(java.lang.String) + */ + public Gravatar loadGravatarByEmail(String email) throws IOException { + return loadGravatarByHash(GravatarUtils.getHash(email)); + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#getGravatarByHash(java.lang.String) + */ + public Gravatar getGravatarByHash(String hash) { + return hash != null && avatars != null ? this.avatars.get(hash) : null; + } + + /** + * @see org.eclipse.mylyn.internal.commons.identity.gravatar.IGravatarStore#getGravatarByEmail(java.lang.String) + */ + public Gravatar getGravatarByEmail(String email) { + return getGravatarByHash(GravatarUtils.getHash(email)); + } + + /** + * @see org.eclipse.core.runtime.jobs.ISchedulingRule#contains(org.eclipse.core.runtime.jobs.ISchedulingRule) + */ + public boolean contains(ISchedulingRule rule) { + return this == rule; + } + + /** + * @see org.eclipse.core.runtime.jobs.ISchedulingRule#isConflicting(org.eclipse.core.runtime.jobs.ISchedulingRule) + */ + public boolean isConflicting(ISchedulingRule rule) { + return this == rule; + } +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarUtils.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarUtils.java new file mode 100644 index 0000000..c6d0f4b --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/GravatarUtils.java @@ -0,0 +1,116 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. and others + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + * Tasktop Technologies - improvements + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +import java.io.UnsupportedEncodingException; +import java.math.BigInteger; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.util.Arrays; +import java.util.Locale; + +import org.eclipse.core.runtime.IAdaptable; + +/** + * Gravatar utililites. + * + * @author Kevin Sawicki (kevin@github.com) + */ +public abstract class GravatarUtils { + + private static String digest(String value) { + String hashed = null; + try { + byte[] input = value.getBytes(IGravatarConstants.CHARSET); + byte[] digested = MessageDigest.getInstance(IGravatarConstants.HASH_ALGORITHM).digest(input); + hashed = new BigInteger(1, digested).toString(16); + int padding = IGravatarConstants.HASH_LENGTH - hashed.length(); + if (padding > 0) { + char[] zeros = new char[padding]; + Arrays.fill(zeros, '0'); + hashed = new String(zeros) + hashed; + } + } catch (NoSuchAlgorithmException e) { + hashed = null; + } catch (UnsupportedEncodingException e) { + hashed = null; + } + return hashed; + } + + /** + * Get hash for object by attempting to adapt it to an {@link IGravatarHashProvider} and fall back on + * {@link Object#toString()} value if adaptation fails and {@link Object#toString()} is or can be transformed into a + * valid hash. + * + * @param element + * @return hash + */ + public String getAdaptedHash(Object element) { + if (element == null) { + return null; + } + + String hash = null; + IGravatarHashProvider provider = null; + if (element instanceof IGravatarHashProvider) { + provider = (IGravatarHashProvider) element; + } else if (element instanceof IAdaptable) { + provider = (IGravatarHashProvider) ((IAdaptable) element).getAdapter(IGravatarHashProvider.class); + } + if (provider != null) { + hash = provider.getGravatarHash(); + } else { + String potentialHash = element.toString(); + if (isValidHash(potentialHash)) { + hash = potentialHash; + } else { + hash = getHash(potentialHash); + } + } + return hash; + } + + /** + * Is the specified string a valid graavatar hash? + * + * @param hash + * @return true if valid hash, false otherwise + */ + public static boolean isValidHash(String hash) { + return hash != null && hash.length() == IGravatarConstants.HASH_LENGTH + && IGravatarConstants.HASH_PATTERN.matcher(hash).matches(); + } + + /** + * Get gravatar hash for specified e-mail address + * + * @param email + * @return hash + */ + public static String getHash(String email) { + String hash = null; + if (email != null) { + email = email.trim().toLowerCase(Locale.US); + if (email.length() > 0) { + hash = digest(email); + } + } + return hash; + } + + public static boolean isValidEmail(String alias) { + return alias != null && alias.contains("@"); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarCallback.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarCallback.java new file mode 100644 index 0000000..9ae9438 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarCallback.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +/** + * Callback interface for when gravatar loading completes or fails. + * + * @author Kevin Sawicki (kevin@github.com) + */ +public interface IGravatarCallback { + + /** + * Gravatar loaded successfully + * + * @param avatar + */ + void loaded(Gravatar avatar); + + /** + * Gravatar loading failed + * + * @param exception + */ + void error(Exception exception); + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarConstants.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarConstants.java new file mode 100644 index 0000000..40bdaa3 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarConstants.java @@ -0,0 +1,53 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +import java.util.regex.Pattern; + +/** + * Gravatar constants. + * + * @author Kevin Sawicki (kevin@github.com) + */ +public interface IGravatarConstants { + + /** + * URL + */ + String URL = "http://www.gravatar.com/avatar/"; //$NON-NLS-1$ + + /** + * HASH_REGEX + */ + String HASH_REGEX = "[0-9a-f]{32}"; //$NON-NLS-1$ + + /** + * HASH_PATTERN + */ + Pattern HASH_PATTERN = Pattern.compile(HASH_REGEX); + + /** + * HASH_LENGTH + */ + int HASH_LENGTH = 32; + + /** + * HASH_ALGORITHM + */ + String HASH_ALGORITHM = "MD5"; //$NON-NLS-1$ + + /** + * Charset used for hashing + */ + String CHARSET = "CP1252"; //$NON-NLS-1$ + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarHashProvider.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarHashProvider.java new file mode 100644 index 0000000..332a4a9 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/IGravatarHashProvider.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +/** + * Interface for providing a gravatar hash. + * + * @author Kevin Sawicki (kevin@github.com) + */ +public interface IGravatarHashProvider { + + /** + * Get hash for gravatar lookup + * + * @return gravatar hash + */ + String getGravatarHash(); + +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/Messages.java b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/Messages.java new file mode 100644 index 0000000..2bb7909 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/Messages.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2011 GitHub Inc. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Kevin Sawicki (GitHub Inc.) - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.identity.gravatar; + +import org.eclipse.osgi.util.NLS; + +/** + * NLS + * + * @author Kevin Sawicki (kevin@github.com) + */ +public class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.mylyn.commons.identity.gravatar.messages"; //$NON-NLS-1$ + + /** + * GravatarStore_LoadingAvatar + */ + public static String GravatarStore_LoadingAvatar; + + /** + * GravatarStore_RefreshJobName + */ + public static String GravatarStore_RefreshJobName; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/messages.properties b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/messages.properties new file mode 100644 index 0000000..1ac24af --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.identity/src/org/eclipse/mylyn/internal/commons/identity/gravatar/messages.properties @@ -0,0 +1,2 @@ +GravatarStore_LoadingAvatar=Loading gravatar for {0} +GravatarStore_RefreshJobName=Refreshing gravatars diff --git a/stubs/org.eclipse.mylyn.commons.notifications/.project b/stubs/org.eclipse.mylyn.commons.notifications/.project index d899a29..1250d04 100644 --- a/stubs/org.eclipse.mylyn.commons.notifications/.project +++ b/stubs/org.eclipse.mylyn.commons.notifications/.project @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <projectDescription> - <name>org.eclipse.mylyn.commons.notifications</name> + <name>org.eclipse.mylyn.commons.notifications-stub</name> <comment></comment> <projects> </projects> diff --git a/stubs/org.eclipse.mylyn.commons.notifications/META-INF/MANIFEST.MF b/stubs/org.eclipse.mylyn.commons.notifications/META-INF/MANIFEST.MF index ecebc28..929b4d1 100644 --- a/stubs/org.eclipse.mylyn.commons.notifications/META-INF/MANIFEST.MF +++ b/stubs/org.eclipse.mylyn.commons.notifications/META-INF/MANIFEST.MF @@ -2,8 +2,20 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.mylyn.commons.notifications;singleton:=true -Bundle-Version: 0.9.0.v20111206-0100 +Bundle-Version: 0.9.0.v20120225-0100 Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Export-Package: org.eclipse.mylyn.commons.ui.notifications;x-internal:=true, + org.eclipse.mylyn.internal.commons.ui.notifications;x-internal:=true, + org.eclipse.mylyn.internal.commons.ui.notifications.popup;x-internal:=true +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.jface, + org.eclipse.ui, + org.eclipse.ui.workbench, + org.eclipse.mylyn.commons.core;bundle-version="3.5.0", + org.eclipse.mylyn.commons.ui;bundle-version="3.5.0", + org.eclipse.ui.forms Bundle-ClassPath: . Bundle-Localization: plugin +Bundle-ActivationPolicy: lazy +Bundle-Activator: org.eclipse.mylyn.internal.commons.ui.notifications.NotificationsPlugin diff --git a/stubs/org.eclipse.mylyn.commons.notifications/pom.xml b/stubs/org.eclipse.mylyn.commons.notifications/pom.xml index e50f9b1..6a4403d 100644 --- a/stubs/org.eclipse.mylyn.commons.notifications/pom.xml +++ b/stubs/org.eclipse.mylyn.commons.notifications/pom.xml @@ -8,7 +8,7 @@ <version>3.7.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.mylyn.commons.notifications</artifactId> - <version>0.9.0.v20111206-0100</version> + <version>0.9.0.v20120225-0100</version> <packaging>eclipse-plugin</packaging> <build> <plugins> diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/.placeholder b/stubs/org.eclipse.mylyn.commons.notifications/src/.placeholder deleted file mode 100644 index e69de29..0000000 --- a/stubs/org.eclipse.mylyn.commons.notifications/src/.placeholder +++ b/dev/null diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/AbstractNotification.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/AbstractNotification.java new file mode 100644 index 0000000..e933ccb --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/AbstractNotification.java @@ -0,0 +1,49 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.notifications; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.swt.graphics.Image; + +/** + * @author Rob Elves + * @author Mik Kersten + */ +public abstract class AbstractNotification implements Comparable<AbstractNotification>, IAdaptable { + + private final String eventId; + + public AbstractNotification(String eventId) { + Assert.isNotNull(eventId); + this.eventId = eventId; + } + + public String getEventId() { + return eventId; + } + + public abstract void open(); + + public abstract String getDescription(); + + public abstract String getLabel(); + + public abstract Image getNotificationImage(); + + public abstract Image getNotificationKindImage(); + + public Object getToken() { + return null; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/INotificationService.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/INotificationService.java new file mode 100644 index 0000000..dc607a7 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/INotificationService.java @@ -0,0 +1,25 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.notifications; + +import java.util.List; + +/** + * @author Steffen Pingel + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface INotificationService { + + public void notify(List<? extends AbstractNotification> notifications); + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/NotificationSink.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/NotificationSink.java new file mode 100644 index 0000000..3694ff2 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/NotificationSink.java @@ -0,0 +1,21 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.notifications; + +/** + * @author Steffen Pingel + */ +public abstract class NotificationSink { + + public abstract void notify(NotificationSinkEvent event); + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/NotificationSinkEvent.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/NotificationSinkEvent.java new file mode 100644 index 0000000..a328eaa --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/NotificationSinkEvent.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.notifications; + +import java.util.List; + +/** + * @author Steffen Pingel + */ +public class NotificationSinkEvent { + + private final List<AbstractNotification> notifications; + + public NotificationSinkEvent(List<AbstractNotification> notifications) { + this.notifications = notifications; + } + + public List<AbstractNotification> getNotifications() { + return notifications; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/Notifications.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/Notifications.java new file mode 100644 index 0000000..d955c81 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/commons/ui/notifications/Notifications.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.notifications; + +import org.eclipse.mylyn.internal.commons.ui.notifications.NotificationsPlugin; + +/** + * @author Steffen Pingel + */ +public final class Notifications { + + private Notifications() { + // do not instantiate + } + + public static INotificationService getService() { + return NotificationsPlugin.getDefault().getService(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/Messages.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/Messages.java new file mode 100644 index 0000000..7a5f257 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/Messages.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.mylyn.internal.commons.ui.notifications.messages"; //$NON-NLS-1$ + + public static String NotificationsPreferencesPage_Descriptions_Label; + + public static String NotificationsPreferencesPage_Enable_Notifications_Text; + + public static String NotificationsPreferencesPage_Events_Label; + + public static String NotificationsPreferencesPage_Notifiers_Label; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationAction.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationAction.java new file mode 100644 index 0000000..9876076 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationAction.java @@ -0,0 +1,51 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; + +/** + * Describes how a {@link NotificationEvent} is handled. {@link NotificationAction}s store enablement and parameters + * that determine how the {@link NotificationSink} executes the action. + * + * @author Steffen Pingel + */ +public class NotificationAction { + + private boolean selected; + + private final NotificationSinkDescriptor sinkDescriptor; + + public NotificationAction(NotificationSinkDescriptor sinkDescriptor) { + Assert.isNotNull(sinkDescriptor); + this.sinkDescriptor = sinkDescriptor; + } + + public NotificationSinkDescriptor getSinkDescriptor() { + return sinkDescriptor; + } + + public boolean isSelected() { + return selected; + } + + public void setSelected(boolean selected) { + this.selected = selected; + } + + @Override + public String toString() { + return sinkDescriptor.getLabel(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationCategory.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationCategory.java new file mode 100644 index 0000000..a6c5797 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationCategory.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.IConfigurationElement; + +/** + * @author Steffen Pingel + */ +public class NotificationCategory extends NotificationElement { + + private final List<NotificationEvent> events; + + public NotificationCategory(IConfigurationElement element) { + super(element); + this.events = new ArrayList<NotificationEvent>(); + } + + public void addEvent(NotificationEvent event) { + event.setCategory(this); + events.add(event); + } + + public List<NotificationEvent> getEvents() { + return events; + } + + public void removeEvent(NotificationEvent event) { + event.setCategory(null); + events.remove(event); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationElement.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationElement.java new file mode 100644 index 0000000..795e32a --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationElement.java @@ -0,0 +1,80 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.plugin.AbstractUIPlugin; + +/** + * @author Steffen Pingel + */ +public class NotificationElement { + + protected final IConfigurationElement element; + + private ImageDescriptor iconDescriptor; + + private final String id; + + private final String label; + + public NotificationElement(IConfigurationElement element) { + Assert.isNotNull(element); + this.element = element; + this.id = element.getAttribute("id"); //$NON-NLS-1$ + this.label = element.getAttribute("label"); //$NON-NLS-1$ + } + + public String getId() { + return id; + } + + public ImageDescriptor getImageDescriptor() { + if (iconDescriptor == null) { + if (element != null) { + String iconPath = element.getAttribute("icon"); //$NON-NLS-1$ + if (iconPath != null) { + iconDescriptor = AbstractUIPlugin.imageDescriptorFromPlugin(element.getContributor().getName(), + iconPath); + } + } + } + return iconDescriptor; + } + + public String getLabel() { + return label; + } + + public String getPluginId() { + return element.getContributor().getName(); + } + + public IStatus validate() { + if (id == null) { + return new Status(IStatus.ERROR, NotificationsPlugin.ID_PLUGIN, NLS.bind( + "Extension {0} contributed by {1} does not specify id attribute", element.getNamespaceIdentifier(), //$NON-NLS-1$ + getPluginId())); //NON-NLS-1$ + } else if (label == null) { + return new Status(IStatus.ERROR, NotificationsPlugin.ID_PLUGIN, NLS.bind( + "Extension {0} contributed by {1} does not specify label attribute", //$NON-NLS-1$ + element.getNamespaceIdentifier(), getPluginId())); //NON-NLS-1$ + } + return Status.OK_STATUS; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationEvent.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationEvent.java new file mode 100644 index 0000000..92527ed --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationEvent.java @@ -0,0 +1,127 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + * Itema AS - bug 331424 handle default event-sink action associations + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.ArrayList; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.Platform; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; + +/** + * Describes an event that is handled through a notification. The handling of event is stored in + * {@link NotificationAction} objects that delegate to {@link NotificationSink} objects for the handling of actual + * events. + * + * @author Steffen Pingel + * @author Torkild U. Resheim + */ +public class NotificationEvent extends NotificationElement { + + private static final String EXTENSION_POINT_ID = "org.eclipse.mylyn.commons.notifications.notifications"; //$NON-NLS-1$ + + private NotificationCategory category; + + private boolean selected; + + private final ArrayList<String> defaultSinks; + + /** + * Tests whether or not the event should per default be handled by the sink with the specified identifier. + * + * @param sinkId + * the sink identifier + * @return <code>true</code> if the + */ + public boolean defaultHandledBySink(String sinkId) { + if (defaultSinks.isEmpty() || defaultSinks.contains(sinkId)) { + return true; + } + return false; + } + + public NotificationEvent(IConfigurationElement element) { + super(element); + defaultSinks = new ArrayList<String>(); + IConfigurationElement[] children = element.getChildren("defaultHandler"); //$NON-NLS-1$ + for (IConfigurationElement child : children) { + defaultSinks.add(child.getAttribute("sinkId")); //$NON-NLS-1$ + } + doEventMappings(); + } + + private void doEventMappings() { + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint point = registry.getExtensionPoint(EXTENSION_POINT_ID); + if (point != null) { + IConfigurationElement[] elements = point.getConfigurationElements(); + for (IConfigurationElement mapping : elements) { + if (mapping.getName().equals("eventMapping")) { //$NON-NLS-1$ + String eventIds = mapping.getAttribute("eventIds"); //$NON-NLS-1$ + String[] list = eventIds.split(","); //$NON-NLS-1$ + for (String item : list) { + if (wildCardMatch(getId(), item)) { + defaultSinks.add(mapping.getAttribute("sinkId")); //$NON-NLS-1$ + } + } + } + } + } + } + + private boolean wildCardMatch(String text, String pattern) { + String[] cards = pattern.split("\\*"); //$NON-NLS-1$ + for (String card : cards) { + int idx = text.indexOf(card); + if (idx == -1) { + return false; + } + text = text.substring(idx + card.length()); + } + + return true; + } + + public NotificationCategory getCategory() { + return category; + } + + public String getCategoryId() { + return element.getAttribute("categoryId"); //$NON-NLS-1$ + } + + public String getDescription() { + IConfigurationElement[] children = element.getChildren("description"); //$NON-NLS-1$ + if (children.length > 0) { + return children[0].getValue(); + } + return ""; //$NON-NLS-1$ + } + + public void setCategory(NotificationCategory category) { + this.category = category; + } + + @Deprecated + public boolean isSelected() { + return selected; + } + + @Deprecated + public void setSelected(boolean selected) { + this.selected = selected; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationHandler.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationHandler.java new file mode 100644 index 0000000..7aa94a7 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationHandler.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.List; + +/** + * Manages actions that are triggered when a {@link NotificationEvent} occurs. + * + * @author Steffen Pingel + */ +public class NotificationHandler { + + private final List<NotificationAction> actions; + + private final NotificationEvent event; + + public NotificationHandler(NotificationEvent event, List<NotificationAction> actions) { + this.event = event; + this.actions = actions; + } + + public List<NotificationAction> getActions() { + return actions; + } + + public NotificationEvent getEvent() { + return event; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationModel.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationModel.java new file mode 100644 index 0000000..4d10e90 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationModel.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + * Itema AS - bug 330064 notification filtering and model persistence + * Itema AS - bug 331424 handle default event-sink action associations + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.ui.IMemento; + +/** + * @author Steffen Pingel + * @author Torkild U. Resheim + */ +public class NotificationModel { + + private boolean dirty; + + private Map<String, NotificationHandler> handlerByEventId; + + public NotificationModel(IMemento memento) { + initialize(memento); + } + + void initialize(IMemento memento) { + this.handlerByEventId = new HashMap<String, NotificationHandler>(); + // We need the handlerByEventId map to be populated early + for (NotificationCategory category : getCategories()) { + for (NotificationEvent event : category.getEvents()) { + getOrCreateNotificationHandler(event); + } + } + if (memento != null) { + load(memento); + } + } + + public Collection<NotificationCategory> getCategories() { + return NotificationsExtensionReader.getCategories(); + } + + public NotificationHandler getNotificationHandler(String eventId) { + return handlerByEventId.get(eventId); + } + + public NotificationHandler getOrCreateNotificationHandler(NotificationEvent event) { + NotificationHandler handler = getNotificationHandler(event.getId()); + if (handler == null) { + handler = new NotificationHandler(event, getActions(event)); + handlerByEventId.put(event.getId(), handler); + } + return handler; + } + + private List<NotificationAction> getActions(NotificationEvent event) { + List<NotificationSinkDescriptor> descriptors = NotificationsExtensionReader.getSinks(); + List<NotificationAction> actions = new ArrayList<NotificationAction>(descriptors.size()); + for (NotificationSinkDescriptor descriptor : descriptors) { + NotificationAction action = new NotificationAction(descriptor); + if (event.defaultHandledBySink(descriptor.getId())) { + action.setSelected(true); + } + actions.add(action); + } + return actions; + } + + public boolean isDirty() { + return dirty; + } + + public boolean isSelected(NotificationEvent event) { + NotificationHandler handler = getOrCreateNotificationHandler(event); + for (NotificationAction action : handler.getActions()) { + if (action.isSelected()) { + return true; + } + } + return false; + } + + /** + * Stores the selected state of events and sinks. + * + * @param memento + * the memento to store in. + */ + public void save(IMemento memento) { + for (Entry<String, NotificationHandler> entry : handlerByEventId.entrySet()) { + IMemento event = memento.createChild("event"); //$NON-NLS-1$ + event.putString("id", entry.getKey()); //$NON-NLS-1$ + List<NotificationAction> actions = entry.getValue().getActions(); + for (NotificationAction notificationAction : actions) { + IMemento action = event.createChild("action"); //$NON-NLS-1$ + action.putBoolean("selected", notificationAction.isSelected()); //$NON-NLS-1$ + action.putString("sink", notificationAction.getSinkDescriptor().getId()); //$NON-NLS-1$ + } + } + setDirty(false); + } + + /** + * Updates the notification model with selected states from the memento instance. + * + * @param memento + */ + private void load(IMemento memento) { + Assert.isNotNull(memento); + for (IMemento mEvent : memento.getChildren("event")) { //$NON-NLS-1$ + for (NotificationCategory category : getCategories()) { + for (NotificationEvent event : category.getEvents()) { + if (event.getId().equals(mEvent.getString("id"))) { //$NON-NLS-1$ + NotificationHandler handler = getOrCreateNotificationHandler(event); + List<NotificationAction> actions = handler.getActions(); + for (NotificationAction notificationAction : actions) { + IMemento[] mActions = mEvent.getChildren("action"); //$NON-NLS-1$ + for (IMemento mAction : mActions) { + if (notificationAction.getSinkDescriptor().getId().equals(mAction.getString("sink"))) { //$NON-NLS-1$ + notificationAction.setSelected(mAction.getBoolean("selected")); //$NON-NLS-1$ + } + } + } + } + } + } + } + } + + public void setDirty(boolean dirty) { + this.dirty = dirty; + } + + /** + * Updates the state of the notification handlers depending on their child notification action states. + */ + @Deprecated + public void updateStates() { + Collection<NotificationHandler> handlers = handlerByEventId.values(); + for (NotificationHandler notificationHandler : handlers) { + List<NotificationAction> actions = notificationHandler.getActions(); + boolean selected = false; + for (NotificationAction notificationAction : actions) { + if (notificationAction.isSelected()) { + selected = true; + break; + } + } + notificationHandler.getEvent().setSelected(selected); + } + } + + public void setNotificationHandler(String eventId, NotificationHandler handler) { + handlerByEventId.put(eventId, handler); + setDirty(true); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationService.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationService.java new file mode 100644 index 0000000..3e57558 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationService.java @@ -0,0 +1,91 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + * Itema AS - bug 330064 notification filtering and model persistence + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylyn.commons.core.StatusHandler; +import org.eclipse.mylyn.commons.ui.notifications.AbstractNotification; +import org.eclipse.mylyn.commons.ui.notifications.INotificationService; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSinkEvent; + +/** + * @author Steffen Pingel + * @author Torkild U. Resheim + */ +public class NotificationService implements INotificationService { + + public NotificationService() { + } + + /** + * Notify sinks about the. + */ + public void notify(List<? extends AbstractNotification> notifications) { + // Return if notifications are not globally enabled. + if (!NotificationsPlugin.getDefault() + .getPreferenceStore() + .getBoolean(NotificationsPlugin.PREF_NOTICATIONS_ENABLED)) { + return; + } + // For each sink assemble a list of notifications that are not blocked + // and pass these along. + HashMap<NotificationSink, ArrayList<AbstractNotification>> filtered = new HashMap<NotificationSink, ArrayList<AbstractNotification>>(); + for (AbstractNotification notification : notifications) { + String id = notification.getEventId(); + NotificationHandler handler = NotificationsPlugin.getDefault().getModel().getNotificationHandler(id); + if (handler != null) { + List<NotificationAction> actions = handler.getActions(); + for (NotificationAction action : actions) { + if (action.isSelected()) { + NotificationSink sink = action.getSinkDescriptor().getSink(); + if (sink != null) { + ArrayList<AbstractNotification> list = filtered.get(sink); + if (list == null) { + list = new ArrayList<AbstractNotification>(); + filtered.put(sink, list); + } + list.add(notification); + } + } + } + } + } + // Go through all the sinks that have notifications to display and let + // them do their job. + for (Entry<NotificationSink, ArrayList<AbstractNotification>> entry : filtered.entrySet()) { + final NotificationSink sink = entry.getKey(); + final NotificationSinkEvent event = new NotificationSinkEvent(new ArrayList<AbstractNotification>( + entry.getValue())); + SafeRunner.run(new ISafeRunnable() { + public void handleException(Throwable e) { + StatusHandler.log(new Status(IStatus.WARNING, NotificationsPlugin.ID_PLUGIN, "Sink failed: " //$NON-NLS-1$ + + sink.getClass(), e)); + } + + public void run() throws Exception { + sink.notify(event); + } + }); + } + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationSinkDescriptor.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationSinkDescriptor.java new file mode 100644 index 0000000..0678424 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationSinkDescriptor.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.statushandlers.StatusManager; + +/** + * @author Steffen Pingel + */ +public class NotificationSinkDescriptor extends NotificationElement { + + private NotificationSink sink; + + private Status status; + + public NotificationSinkDescriptor(IConfigurationElement element) { + super(element); + } + + public NotificationSink getSink() { + if (sink != null || status != null) { + return sink; + } + + try { + Object object = element.createExecutableExtension("class"); //$NON-NLS-1$ + if (object instanceof NotificationSink) { + sink = (NotificationSink) object; + return sink; + } else { + status = new Status(IStatus.ERROR, NotificationsPlugin.ID_PLUGIN, NLS.bind( + "Sink ''{0}'' does not extend expected class for extension contributed by {1}", //$NON-NLS-1$ + object.getClass().getCanonicalName(), getPluginId())); + } + } catch (Throwable e) { + status = new Status(IStatus.ERROR, NotificationsPlugin.ID_PLUGIN, NLS.bind( + "Sink failed to load for extension contributed by {0}", getPluginId()), e); //$NON-NLS-1$ + } + + StatusManager.getManager().handle(status); + return null; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsExtensionReader.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsExtensionReader.java new file mode 100644 index 0000000..b0b1a0a --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsExtensionReader.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + * Itema AS - bug 330064 notification filtering and model persistence + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; + +import org.eclipse.core.runtime.IConfigurationElement; +import org.eclipse.core.runtime.IExtension; +import org.eclipse.core.runtime.IExtensionPoint; +import org.eclipse.core.runtime.IExtensionRegistry; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.MultiStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.osgi.util.NLS; +import org.eclipse.ui.statushandlers.StatusManager; + +/** + * @author Steffen Pingel + * @author Torkild U. Resheim + */ +public class NotificationsExtensionReader { + + private static boolean errorLogged = false; + + static List<NotificationSinkDescriptor> sinks; + + private static Collection<NotificationCategory> categories; + + /** + * Returns a list of notification categories, each containing their belonging notification events. Once initialised + * the same list will be returned upon subsequent calls of this method. + * + * @return a list of notification categories. + * @see NotificationModel#save(org.eclipse.ui.IMemento) + * @see NotificationModel#load(org.eclipse.ui.IMemento) + */ + public static Collection<NotificationCategory> getCategories() { + if (categories != null) { + return categories; + } + HashMap<String, NotificationCategory> categoryById = new HashMap<String, NotificationCategory>(); + + MultiStatus result = new MultiStatus(NotificationsPlugin.ID_PLUGIN, 0, + "Notifcation extensions failed to load", null); //$NON-NLS-1$ + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint repositoriesExtensionPoint = registry.getExtensionPoint(NotificationsPlugin.ID_PLUGIN + + ".notifications"); //$NON-NLS-1$ + IExtension[] extensions = repositoriesExtensionPoint.getExtensions(); + for (IExtension extension : extensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for (IConfigurationElement element : elements) { + if ("category".equals(element.getName())) { //$NON-NLS-1$ + NotificationCategory category = new NotificationCategory(element); + IStatus status = category.validate(); + if (status.isOK()) { + categoryById.put(category.getId(), category); + } else { + result.add(status); + } + } + } + for (IConfigurationElement element : elements) { + if ("event".equals(element.getName())) { //$NON-NLS-1$ + NotificationEvent event = new NotificationEvent(element); + IStatus status = event.validate(); + if (status.isOK()) { + NotificationCategory category = categoryById.get(event.getCategoryId()); + if (category != null) { + category.addEvent(event); + } else { + result.add(new Status( + IStatus.ERROR, + NotificationsPlugin.ID_PLUGIN, + NLS.bind( + "Extension {0} contributed by {1} specify unknown category ''{2}''", new String[] { element.getNamespaceIdentifier(), element.getContributor().getName(), event.getCategoryId() }))); //NON-NLS-1$ //$NON-NLS-1$ + } + } else { + result.add(status); + } + } + } + } + + if (!result.isOK() && !errorLogged) { + StatusManager.getManager().handle(result); + errorLogged = true; + } + + categories = categoryById.values(); + return categories; + } + + public static List<NotificationSinkDescriptor> getSinks() { + if (sinks != null) { + return sinks; + } + + sinks = new ArrayList<NotificationSinkDescriptor>(); + + MultiStatus result = new MultiStatus(NotificationsPlugin.ID_PLUGIN, 0, + "Notifcation extensions failed to load", null); //$NON-NLS-1$ + + IExtensionRegistry registry = Platform.getExtensionRegistry(); + IExtensionPoint repositoriesExtensionPoint = registry.getExtensionPoint(NotificationsPlugin.ID_PLUGIN + + ".notifications"); //$NON-NLS-1$ + IExtension[] extensions = repositoriesExtensionPoint.getExtensions(); + for (IExtension extension : extensions) { + IConfigurationElement[] elements = extension.getConfigurationElements(); + for (IConfigurationElement element : elements) { + if ("sink".equals(element.getName())) { //$NON-NLS-1$ + NotificationSinkDescriptor descriptor = new NotificationSinkDescriptor(element); + IStatus status = descriptor.validate(); + if (status.isOK()) { + sinks.add(descriptor); + } else { + result.add(status); + } + } + } + } + + if (!result.isOK()) { + StatusManager.getManager().handle(result); + } + + return sinks; + } +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPlugin.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPlugin.java new file mode 100644 index 0000000..4325346 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPlugin.java @@ -0,0 +1,131 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; + +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.WorkbenchException; +import org.eclipse.ui.XMLMemento; +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * @author Steffen Pingel + */ +public class NotificationsPlugin extends AbstractUIPlugin { + + public static final String ID_PLUGIN = "org.eclipse.mylyn.commons.notifications"; //$NON-NLS-1$ + + public static final String PREF_NOTICATIONS_ENABLED = "notifications.enabled"; //$NON-NLS-1$ + + private static NotificationsPlugin instance; + + public static NotificationsPlugin getDefault() { + return instance; + } + + private NotificationModel model; + + private NotificationService service; + + public NotificationModel createModelWorkingCopy() { + IMemento memento = null; + File file = getModelFile().toFile(); + if (file.exists()) { + try { + FileReader reader = new FileReader(file); + try { + memento = XMLMemento.createReadRoot(reader); + } finally { + reader.close(); + } + } catch (IOException e) { + getLog().log(new Status(IStatus.ERROR, ID_PLUGIN, "Unexpected error restoring notification state", e)); //$NON-NLS-1$ + } catch (WorkbenchException e) { + getLog().log(new Status(IStatus.ERROR, ID_PLUGIN, "Unexpected error restoring notification state", e)); //$NON-NLS-1$ + } + } + return new NotificationModel(memento); + } + + public NotificationModel getModel() { + if (model == null) { + model = createModelWorkingCopy(); + } + return model; + } + + public NotificationService getService() { + if (service == null) { + service = new NotificationService(); + } + return service; + } + + public void saveModel() { + if (model != null && model.isDirty()) { + save(model); + } + } + + public void saveWorkingCopy(NotificationModel workingCopy) { + XMLMemento memento = save(workingCopy); + if (this.model != null) { + // reload model + this.model.initialize(memento); + } + } + + @Override + public void start(BundleContext context) throws Exception { + instance = this; + super.start(context); + } + + @Override + public void stop(BundleContext context) throws Exception { + super.stop(context); + instance = null; + } + + private XMLMemento save(NotificationModel model) { + XMLMemento memento = XMLMemento.createWriteRoot("notifications"); //$NON-NLS-1$ + model.save(memento); + FileWriter writer; + try { + writer = new FileWriter(getModelFile().toFile()); + try { + memento.save(writer); + } finally { + writer.close(); + } + } catch (IOException e) { + getLog().log(new Status(IStatus.ERROR, ID_PLUGIN, "Unexpected error saving notification state", e)); //$NON-NLS-1$ + } + return memento; + } + + protected IPath getModelFile() { + IPath stateLocation = Platform.getStateLocation(getBundle()); + IPath cacheFile = stateLocation.append("notifications.xml"); //$NON-NLS-1$ + return cacheFile; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesInitializer.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesInitializer.java new file mode 100644 index 0000000..299a202 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesInitializer.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import org.eclipse.core.runtime.preferences.AbstractPreferenceInitializer; +import org.eclipse.jface.preference.IPreferenceStore; + +/** + * @author Steffen Pingel + */ +public class NotificationsPreferencesInitializer extends AbstractPreferenceInitializer { + + public NotificationsPreferencesInitializer() { + // ignore + } + + @Override + public void initializeDefaultPreferences() { + IPreferenceStore preferencesStore = NotificationsPlugin.getDefault().getPreferenceStore(); + preferencesStore.setDefault(NotificationsPlugin.PREF_NOTICATIONS_ENABLED, true); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesPage.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesPage.java new file mode 100644 index 0000000..82d4e54 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/NotificationsPreferencesPage.java @@ -0,0 +1,406 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + * Itema AS - bug 329897 select event type on open if available + * Itema AS - bug 330064 notification filtering and model persistence + * Itema AS - bug 331424 handle default event-sink action associations + *******************************************************************************/ +package org.eclipse.mylyn.internal.commons.ui.notifications; + +import java.util.Collection; +import java.util.List; + +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.preference.PreferencePage; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.CheckStateChangedEvent; +import org.eclipse.jface.viewers.CheckboxTableViewer; +import org.eclipse.jface.viewers.ICheckStateListener; +import org.eclipse.jface.viewers.ICheckStateProvider; +import org.eclipse.jface.viewers.IElementComparer; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredContentProvider; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonImages; +import org.eclipse.mylyn.internal.provisional.commons.ui.SubstringPatternFilter; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.layout.FillLayout; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Group; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.IWorkbenchPreferencePage; +import org.eclipse.ui.dialogs.FilteredTree; + +/** + * @author Steffen Pingel + * @author Torkild Ulvøy Resheim + */ +public class NotificationsPreferencesPage extends PreferencePage implements IWorkbenchPreferencePage { + + /** + * We need this in order to make sure that the correct element is selected in the {@link TreeViewer} when the + * selection is set. + * + * @author Torkild Ulvøy Resheim + */ + public class NotificationEventComparer implements IElementComparer { + + public boolean equals(Object a, Object b) { + if (a instanceof NotificationEvent && b instanceof NotificationEvent) { + String idA = ((NotificationEvent) a).getId(); + String idB = ((NotificationEvent) b).getId(); + return (idA.equals(idB)); + } + return a.equals(b); + } + + public int hashCode(Object element) { + return element.hashCode(); + } + + } + + private static final Object[] EMPTY = new Object[0]; + + private final class EventContentProvider implements ITreeContentProvider { + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // ignore + } + + public void dispose() { + // ignore + } + + public boolean hasChildren(Object element) { + if (element instanceof NotificationCategory) { + return ((NotificationCategory) element).getEvents().size() > 0; + } + return false; + } + + public Object getParent(Object element) { + if (element instanceof NotificationEvent) { + return ((NotificationEvent) element).getCategory(); + } + return null; + } + + public Object[] getElements(Object inputElement) { + if (inputElement instanceof Object[]) { + return (Object[]) inputElement; + } else { + return EMPTY; + } + } + + public Object[] getChildren(Object parentElement) { + if (parentElement instanceof NotificationCategory) { + return ((NotificationCategory) parentElement).getEvents().toArray(); + } + return EMPTY; + } + + } + + private final class NotifiersContentProvider implements IStructuredContentProvider { + + private NotificationHandler handler; + + public void dispose() { + // ignore + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + if (newInput instanceof NotificationHandler) { + handler = (NotificationHandler) newInput; + } else { + handler = null; + } + } + + public Object[] getElements(Object inputElement) { + if (handler != null) { + return handler.getActions().toArray(); + } else { + return EMPTY; + } + } + + } + + public final class EventLabelProvider extends LabelProvider { + + @Override + public String getText(Object element) { + if (element instanceof NotificationElement) { + NotificationElement item = (NotificationElement) element; + return item.getLabel(); + } + return super.getText(element); + } + + @Override + public Image getImage(Object element) { + if (element instanceof NotificationEvent) { + NotificationEvent item = (NotificationEvent) element; + if (model.isSelected(item)) { + return CommonImages.getImage(CommonImages.CHECKED); + } else { + return null; + } + } + if (element instanceof NotificationElement) { + NotificationElement item = (NotificationElement) element; + ImageDescriptor imageDescriptor = item.getImageDescriptor(); + if (imageDescriptor != null) { + return CommonImages.getImage(imageDescriptor); + } + } + return super.getImage(element); + } + } + + private TreeViewer eventsViewer; + + private CheckboxTableViewer notifiersViewer; + + private Button enableNotificationsButton; + + private NotificationModel model; + + private Text descriptionText; + + public NotificationsPreferencesPage() { + } + + @Override + public IPreferenceStore getPreferenceStore() { + return NotificationsPlugin.getDefault().getPreferenceStore(); + } + + @Override + protected Control createContents(Composite parent) { + model = NotificationsPlugin.getDefault().createModelWorkingCopy(); + + Composite composite = new Composite(parent, SWT.NONE); + composite.setLayout(new GridLayout(2, false)); + + enableNotificationsButton = new Button(composite, SWT.CHECK); + GridDataFactory.fillDefaults().span(2, 1).applyTo(enableNotificationsButton); + enableNotificationsButton.setText(Messages.NotificationsPreferencesPage_Enable_Notifications_Text); + enableNotificationsButton.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + updateEnablement(); + } + }); + + Label label = new Label(composite, SWT.NONE); + label.setText(" "); //$NON-NLS-1$ + GridDataFactory.fillDefaults().span(2, 1).applyTo(label); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.NotificationsPreferencesPage_Events_Label); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.NotificationsPreferencesPage_Notifiers_Label); + // Create the tree showing all the various notification types + FilteredTree tree = new FilteredTree(composite, SWT.BORDER, new SubstringPatternFilter(), true); + eventsViewer = tree.getViewer(); + GridDataFactory.fillDefaults().span(1, 2).grab(false, true).applyTo(tree); + eventsViewer.setComparer(new NotificationEventComparer()); + eventsViewer.setContentProvider(new EventContentProvider()); + eventsViewer.setLabelProvider(new EventLabelProvider()); + eventsViewer.setInput(model.getCategories().toArray()); + eventsViewer.expandAll(); + eventsViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + Object input = getDetailsInput((IStructuredSelection) event.getSelection()); + notifiersViewer.setInput(input); + + Object item = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (item instanceof NotificationEvent) { + descriptionText.setText(((NotificationEvent) item).getDescription()); + notifiersViewer.getControl().setEnabled(true); + } else { + descriptionText.setText(" "); //$NON-NLS-1$ + notifiersViewer.getControl().setEnabled(false); + } + } + + private Object getDetailsInput(IStructuredSelection selection) { + Object item = selection.getFirstElement(); + if (item instanceof NotificationEvent) { + return model.getOrCreateNotificationHandler((NotificationEvent) item); + } + return null; + } + }); + // Create the table listing all notification sinks available for the selected event type. + notifiersViewer = CheckboxTableViewer.newCheckList(composite, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, true).applyTo(notifiersViewer.getControl()); + notifiersViewer.setContentProvider(new NotifiersContentProvider()); + notifiersViewer.setLabelProvider(new EventLabelProvider()); + notifiersViewer.addCheckStateListener(new ICheckStateListener() { + public void checkStateChanged(CheckStateChangedEvent event) { + NotificationAction action = (NotificationAction) event.getElement(); + action.setSelected(event.getChecked()); + model.setDirty(true); + eventsViewer.refresh(); + } + }); + notifiersViewer.setCheckStateProvider(new ICheckStateProvider() { + public boolean isChecked(Object element) { + return ((NotificationAction) element).isSelected(); + } + + public boolean isGrayed(Object element) { + return false; + } + }); + notifiersViewer.addSelectionChangedListener(new ISelectionChangedListener() { + public void selectionChanged(SelectionChangedEvent event) { + Object item = ((IStructuredSelection) event.getSelection()).getFirstElement(); + if (item instanceof NotificationAction) { + // TODO show configuration pane + } + } + }); + + Group group = new Group(composite, SWT.NONE); + GridDataFactory.fillDefaults().hint(150, SWT.DEFAULT).grab(true, true).applyTo(group); + group.setText(Messages.NotificationsPreferencesPage_Descriptions_Label); + FillLayout layout = new FillLayout(); + layout.marginHeight = 5; + layout.marginWidth = 5; + group.setLayout(layout); + + descriptionText = new Text(group, SWT.WRAP); + descriptionText.setBackground(group.getBackground()); + +// Button testButton = new Button(composite, SWT.NONE); +// testButton.setText("Test"); +// testButton.addSelectionListener(new SelectionAdapter() { +// @Override +// public void widgetSelected(SelectionEvent e) { +// ISelection selection = eventsViewer.getSelection(); +// if (selection instanceof IStructuredSelection) { +// Object object = ((IStructuredSelection) selection).getFirstElement(); +// if (object instanceof NotificationEvent) { +// final NotificationEvent event = (NotificationEvent) object; +// getControl().getDisplay().asyncExec(new Runnable() { +// public void run() { +// Notifications.getService().notify( +// Collections.singletonList(new TestNotification(event))); +// } +// }); +// } +// } +// } +// +// }); + + reset(); + Dialog.applyDialogFont(composite); + return composite; + } + + @Override + public void applyData(Object data) { + // We may or may not have a NotificationEvent supplied when this + // preference dialog is opened. If we do have this data we want to + // highlight the appropriate instance. + if (data instanceof String) { + String selectedEventId = (String) data; + Collection<NotificationCategory> items = model.getCategories(); + NotificationEvent selectedEvent = null; + for (NotificationCategory notificationCategory : items) { + List<NotificationEvent> event = notificationCategory.getEvents(); + for (NotificationEvent notificationEvent : event) { + if (notificationEvent.getId().equals(selectedEventId)) { + selectedEvent = notificationEvent; + break; + } + } + } + if (selectedEvent != null) { + eventsViewer.setSelection(new StructuredSelection(selectedEvent), true); + } + } + } + + private void updateEnablement() { + boolean enabled = enableNotificationsButton.getSelection(); + eventsViewer.getControl().setEnabled(enabled); + notifiersViewer.getControl().setEnabled(enabled);// FIXME enabled && notifiersViewer.getInput() != null); + descriptionText.setEnabled(enabled); + if (!enabled) { + eventsViewer.setSelection(StructuredSelection.EMPTY); + } + } + + public void init(IWorkbench workbench) { + // ignore + } + + public void reset() { + enableNotificationsButton.setSelection(getPreferenceStore().getBoolean( + NotificationsPlugin.PREF_NOTICATIONS_ENABLED)); + updateEnablement(); + } + + @Override + public boolean performOk() { + getPreferenceStore().setValue(NotificationsPlugin.PREF_NOTICATIONS_ENABLED, + enableNotificationsButton.getSelection()); + if (model.isDirty()) { + NotificationsPlugin.getDefault().saveWorkingCopy(model); + model.setDirty(false); + } + return super.performOk(); + } + + @Override + protected void performDefaults() { + enableNotificationsButton.setSelection(getPreferenceStore().getDefaultBoolean( + NotificationsPlugin.PREF_NOTICATIONS_ENABLED)); + for (NotificationCategory category : model.getCategories()) { + for (NotificationEvent event : category.getEvents()) { + NotificationHandler handler = model.getOrCreateNotificationHandler(event); + for (NotificationAction action : handler.getActions()) { + action.setSelected(event.defaultHandledBySink(action.getSinkDescriptor().getId())); + } + } + } + // assume that the model has become dirty + model.setDirty(true); + // refresh UI + eventsViewer.refresh(); + notifiersViewer.refresh(); + updateEnablement(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/messages.properties b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/messages.properties new file mode 100644 index 0000000..1b85c3c --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/messages.properties @@ -0,0 +1,4 @@ +NotificationsPreferencesPage_Descriptions_Label=Description +NotificationsPreferencesPage_Enable_Notifications_Text=&Enable notifications +NotificationsPreferencesPage_Events_Label=Events: +NotificationsPreferencesPage_Notifiers_Label=Notifiers: diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/Messages.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/Messages.java new file mode 100644 index 0000000..f82bf60 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/Messages.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications.popup; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.mylyn.internal.commons.ui.notifications.popup.messages"; //$NON-NLS-1$ + + public static String PopupNotificationSink_Popup_Noifier_Job_Label; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/NotificationPopup.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/NotificationPopup.java new file mode 100644 index 0000000..b16d44b --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/NotificationPopup.java @@ -0,0 +1,176 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications.popup; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.mylyn.commons.ui.notifications.AbstractNotification; +import org.eclipse.mylyn.internal.commons.ui.CommonsUiPlugin; +import org.eclipse.mylyn.internal.provisional.commons.ui.AbstractNotificationPopup; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonColors; +import org.eclipse.mylyn.internal.provisional.commons.ui.CommonUiUtil; +import org.eclipse.mylyn.internal.provisional.commons.ui.ScalingHyperlink; +import org.eclipse.osgi.util.NLS; +import org.eclipse.swt.SWT; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Display; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.forms.IFormColors; +import org.eclipse.ui.forms.events.HyperlinkAdapter; +import org.eclipse.ui.forms.events.HyperlinkEvent; + +/** + * @author Rob Elves + * @author Mik Kersten + */ +public class NotificationPopup extends AbstractNotificationPopup { + + private static final int NUM_NOTIFICATIONS_TO_DISPLAY = 4; + + private List<AbstractNotification> notifications; + + public NotificationPopup(Shell parent) { + super(parent.getDisplay()); + } + + @Override + protected void createContentArea(Composite parent) { + int count = 0; + for (final AbstractNotification notification : notifications) { + Composite notificationComposite = new Composite(parent, SWT.NO_FOCUS); + GridLayout gridLayout = new GridLayout(2, false); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(notificationComposite); + notificationComposite.setLayout(gridLayout); + notificationComposite.setBackground(parent.getBackground()); + + if (count < NUM_NOTIFICATIONS_TO_DISPLAY) { + final Label notificationLabelIcon = new Label(notificationComposite, SWT.NO_FOCUS); + notificationLabelIcon.setBackground(parent.getBackground()); + notificationLabelIcon.setImage(notification.getNotificationKindImage()); + // FIXME +// if (!(notification instanceof TaskListNotificationReminder)) { +// final AbstractTask task = (AbstractTask) notification.getAdapter(AbstractTask.class); +// if (task != null) { +// notificationLabelIcon.addMouseListener(new MouseAdapter() { +// @Override +// public void mouseUp(MouseEvent e) { +// TasksUiPlugin.getTaskDataManager().setTaskRead(task, true); +// notificationLabelIcon.setImage(null); +// notificationLabelIcon.setToolTipText(null); +// } +// }); +// notificationLabelIcon.setToolTipText(Messages.TaskListNotificationPopup_Mark_Task_Read); +// } +// } + + // FIXME +// final TaskScalingHyperlink itemLink = new TaskScalingHyperlink(notificationComposite, SWT.BEGINNING +// | SWT.NO_FOCUS); + + final ScalingHyperlink itemLink = new ScalingHyperlink(notificationComposite, SWT.BEGINNING + | SWT.NO_FOCUS); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.TOP).applyTo(itemLink); + itemLink.setForeground(CommonColors.HYPERLINK_WIDGET); + itemLink.registerMouseTrackListener(); + itemLink.setText(CommonUiUtil.toLabel(notification.getLabel())); + itemLink.setImage(notification.getNotificationImage()); + itemLink.setBackground(parent.getBackground()); + itemLink.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + notification.open(); + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + Shell windowShell = window.getShell(); + if (windowShell != null) { + if (windowShell.getMinimized()) { + windowShell.setMinimized(false); + } + + windowShell.open(); + windowShell.forceActive(); + } + } + } + }); + + String descriptionText = null; + if (notification.getDescription() != null) { + descriptionText = notification.getDescription(); + } + if (descriptionText != null && !descriptionText.trim().equals("")) { //$NON-NLS-1$ + Label descriptionLabel = new Label(notificationComposite, SWT.NO_FOCUS); + descriptionLabel.setText(CommonUiUtil.toLabel(descriptionText)); + descriptionLabel.setBackground(parent.getBackground()); + GridDataFactory.fillDefaults() + .span(2, SWT.DEFAULT) + .grab(true, false) + .align(SWT.FILL, SWT.TOP) + .applyTo(descriptionLabel); + } + } else { + int numNotificationsRemain = notifications.size() - count; + ScalingHyperlink remainingLink = new ScalingHyperlink(notificationComposite, SWT.NO_FOCUS); + remainingLink.setForeground(CommonColors.HYPERLINK_WIDGET); + remainingLink.registerMouseTrackListener(); + remainingLink.setBackground(parent.getBackground()); + + remainingLink.setText(NLS.bind("{0} more", numNotificationsRemain)); //$NON-NLS-1$ + GridDataFactory.fillDefaults().span(2, SWT.DEFAULT).applyTo(remainingLink); + remainingLink.addHyperlinkListener(new HyperlinkAdapter() { + @Override + public void linkActivated(HyperlinkEvent e) { + // FIXME + // TasksUiUtil.openTasksViewInActivePerspective().setFocus(); + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + Shell windowShell = window.getShell(); + if (windowShell != null) { + windowShell.setMaximized(true); + windowShell.open(); + } + } + } + }); + break; + } + count++; + } + } + + @Override + protected void createTitleArea(Composite parent) { + super.createTitleArea(parent); + } + + public List<AbstractNotification> getNotifications() { + return new ArrayList<AbstractNotification>(notifications); + } + + @Override + protected Color getTitleForeground() { + return CommonsUiPlugin.getDefault().getFormColors(Display.getDefault()).getColor(IFormColors.TITLE); + + } + + public void setContents(List<AbstractNotification> notifications) { + this.notifications = notifications; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/PopupNotificationSink.java b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/PopupNotificationSink.java new file mode 100644 index 0000000..785ba85 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/PopupNotificationSink.java @@ -0,0 +1,163 @@ +/******************************************************************************* + * Copyright (c) 2004, 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.notifications.popup; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; +import java.util.WeakHashMap; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Platform; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.jface.preference.IPreferenceStore; +import org.eclipse.jface.window.Window; +import org.eclipse.mylyn.commons.ui.notifications.AbstractNotification; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSink; +import org.eclipse.mylyn.commons.ui.notifications.NotificationSinkEvent; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IWorkbenchPreferenceConstants; +import org.eclipse.ui.PlatformUI; + +/** + * @author Rob Elves + * @author Steffen Pingel + */ +public class PopupNotificationSink extends NotificationSink { + + private static final long DELAY_OPEN = 1 * 1000; + + private static final boolean runSystem = true; + + private final WeakHashMap<Object, Object> cancelledTokens = new WeakHashMap<Object, Object>(); + + private final Set<AbstractNotification> notifications = new HashSet<AbstractNotification>(); + + private final Set<AbstractNotification> currentlyNotifying = Collections.synchronizedSet(notifications); + + private final Job openJob = new Job(Messages.PopupNotificationSink_Popup_Noifier_Job_Label) { + @Override + protected IStatus run(IProgressMonitor monitor) { + try { + if (Platform.isRunning() && PlatformUI.getWorkbench() != null + && PlatformUI.getWorkbench().getDisplay() != null + && !PlatformUI.getWorkbench().getDisplay().isDisposed()) { + PlatformUI.getWorkbench().getDisplay().asyncExec(new Runnable() { + + public void run() { + collectNotifications(); + + if (popup != null && popup.getReturnCode() == Window.CANCEL) { + List<AbstractNotification> notifications = popup.getNotifications(); + for (AbstractNotification notification : notifications) { + if (notification.getToken() != null) { + cancelledTokens.put(notification.getToken(), null); + } + } + } + + for (Iterator<AbstractNotification> it = currentlyNotifying.iterator(); it.hasNext();) { + AbstractNotification notification = it.next(); + if (notification.getToken() != null + && cancelledTokens.containsKey(notification.getToken())) { + it.remove(); + } + } + + synchronized (PopupNotificationSink.class) { + if (currentlyNotifying.size() > 0) { +// popup.close(); + showPopup(); + } + } + } + }); + } + } finally { + if (popup != null) { + schedule(popup.getDelayClose() / 2); + } + } + + if (monitor.isCanceled()) { + return Status.CANCEL_STATUS; + } + + return Status.OK_STATUS; + } + + }; + + private NotificationPopup popup; + + public PopupNotificationSink() { + } + + private void cleanNotified() { + currentlyNotifying.clear(); + } + + /** public for testing */ + public void collectNotifications() { + } + + /** + * public for testing purposes + */ + public Set<AbstractNotification> getNotifications() { + synchronized (PopupNotificationSink.class) { + return currentlyNotifying; + } + } + + public boolean isAnimationsEnabled() { + IPreferenceStore store = PlatformUI.getPreferenceStore(); + return store.getBoolean(IWorkbenchPreferenceConstants.ENABLE_ANIMATIONS); + } + + @Override + public void notify(NotificationSinkEvent event) { + currentlyNotifying.addAll(event.getNotifications()); + + if (!openJob.cancel()) { + try { + openJob.join(); + } catch (InterruptedException e) { + // ignore + } + } + openJob.setSystem(runSystem); + openJob.schedule(DELAY_OPEN); + } + + public void showPopup() { + if (popup != null) { + popup.close(); + } + + Shell shell = new Shell(PlatformUI.getWorkbench().getDisplay()); + popup = new NotificationPopup(shell); + popup.setFadingEnabled(isAnimationsEnabled()); + List<AbstractNotification> toDisplay = new ArrayList<AbstractNotification>(currentlyNotifying); + Collections.sort(toDisplay); + popup.setContents(toDisplay); + cleanNotified(); + popup.setBlockOnOpen(false); + popup.open(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/messages.properties b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/messages.properties new file mode 100644 index 0000000..5ac2c06 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.notifications/src/org/eclipse/mylyn/internal/commons/ui/notifications/popup/messages.properties @@ -0,0 +1 @@ +PopupNotificationSink_Popup_Noifier_Job_Label=Popup Notifier diff --git a/stubs/org.eclipse.mylyn.commons.repositories/.project b/stubs/org.eclipse.mylyn.commons.repositories/.project index 7a9c8a1..143c958 100644 --- a/stubs/org.eclipse.mylyn.commons.repositories/.project +++ b/stubs/org.eclipse.mylyn.commons.repositories/.project @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <projectDescription> - <name>org.eclipse.mylyn.commons.repositories</name> + <name>org.eclipse.mylyn.commons.repositories-stub</name> <comment></comment> <projects> </projects> diff --git a/stubs/org.eclipse.mylyn.commons.repositories/META-INF/MANIFEST.MF b/stubs/org.eclipse.mylyn.commons.repositories/META-INF/MANIFEST.MF index 5153187..795dc26 100644 --- a/stubs/org.eclipse.mylyn.commons.repositories/META-INF/MANIFEST.MF +++ b/stubs/org.eclipse.mylyn.commons.repositories/META-INF/MANIFEST.MF @@ -2,8 +2,15 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.mylyn.commons.repositories;singleton:=true -Bundle-Version: 0.9.0.v20111206-0100 +Bundle-Version: 0.9.0.v20120225-0100 Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: J2SE-1.5 +Export-Package: org.eclipse.mylyn.commons.repositories;x-internal:=true, + org.eclipse.mylyn.commons.repositories.auth;x-internal:=true, + org.eclipse.mylyn.internal.commons.repositories;x-internal:=true +Require-Bundle: org.eclipse.core.runtime, + org.eclipse.equinox.security, + org.eclipse.mylyn.commons.core, + org.eclipse.mylyn.commons.net;bundle-version="3.5.0" Bundle-ClassPath: . Bundle-Localization: plugin diff --git a/stubs/org.eclipse.mylyn.commons.repositories/pom.xml b/stubs/org.eclipse.mylyn.commons.repositories/pom.xml index d8643b6..54f135e 100644 --- a/stubs/org.eclipse.mylyn.commons.repositories/pom.xml +++ b/stubs/org.eclipse.mylyn.commons.repositories/pom.xml @@ -8,7 +8,7 @@ <version>3.7.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.mylyn.commons.repositories</artifactId> - <version>0.9.0.v20111206-0100</version> + <version>0.9.0.v20120225-0100</version> <packaging>eclipse-plugin</packaging> <build> <plugins> diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/.placehoder b/stubs/org.eclipse.mylyn.commons.repositories/src/.placehoder deleted file mode 100644 index e69de29..0000000 --- a/stubs/org.eclipse.mylyn.commons.repositories/src/.placehoder +++ b/dev/null diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/ILocationService.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/ILocationService.java new file mode 100644 index 0000000..1988c03 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/ILocationService.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories; + +import java.net.Proxy; + +import javax.net.ssl.X509TrustManager; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationCredentials; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationType; +import org.eclipse.mylyn.commons.repositories.auth.ICredentialsStore; + +/** + * @author Steffen Pingel + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface ILocationService { + + // FIXME replace with 3.5 proxy API + public abstract Proxy getProxyForHost(String host, String proxyType); + + public abstract X509TrustManager getTrustManager(); + + public abstract <T extends AuthenticationCredentials> T requestCredentials(AuthenticationType type, + Class<T> credentialsKind, String message, IProgressMonitor monitor); + + public ICredentialsStore getCredentialsStore(String id); + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryCategory.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryCategory.java new file mode 100644 index 0000000..0aa6bb0 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryCategory.java @@ -0,0 +1,73 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories; + +import org.eclipse.core.runtime.PlatformObject; + +/** + * Categories to group repositories of the same kind, e.g. Tasks, Builds or Reviews. + * + * @author Robert Elves + */ +public class RepositoryCategory extends PlatformObject { + + private final String id; + + private final String label; + + private final int rank; + + public static final String ID_CATEGORY_BUGS = "org.eclipse.mylyn.category.bugs"; //$NON-NLS-1$ + + public static final String ID_CATEGORY_BUILDS = "org.eclipse.mylyn.category.build"; //$NON-NLS-1$ + + public static final String ID_CATEGORY_OTHER = "org.eclipse.mylyn.category.other"; //$NON-NLS-1$ + + public static final String ID_CATEGORY_REVIEWS = "org.eclipse.mylyn.category.review"; //$NON-NLS-1$ + + public static final String ID_CATEGORY_TASKS = "org.eclipse.mylyn.category.tasks"; //$NON-NLS-1$ + + public static final String ID_CATEGORY_ALL = "org.eclipse.mylyn.category.all"; //$NON-NLS-1$ + + public static final String ID_CATEGORY_ROOT = "org.eclipse.mylyn.category.root"; //$NON-NLS-1$ + + public RepositoryCategory(String id, String label, int rank) { + this.id = id; + this.label = label; + this.rank = rank; + } + + public String getId() { + return id; + } + + public int compareTo(Object arg0) { + if (arg0 instanceof RepositoryCategory) { + return this.getRank() - ((RepositoryCategory) arg0).getRank(); + } + return 0; + } + + public int getRank() { + return rank; + } + + public String getLabel() { + return label; + } + + @Override + public String toString() { + return getLabel(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryLocation.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryLocation.java new file mode 100644 index 0000000..fbe7c71 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryLocation.java @@ -0,0 +1,358 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; + +import org.eclipse.core.runtime.Assert; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.PlatformObject; +import org.eclipse.equinox.security.storage.StorageException; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationCredentials; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationType; +import org.eclipse.mylyn.commons.repositories.auth.ICredentialsStore; +import org.eclipse.mylyn.internal.commons.repositories.CredentialsFactory; +import org.eclipse.mylyn.internal.commons.repositories.InMemoryCredentialsStore; +import org.eclipse.mylyn.internal.commons.repositories.LocationService; + +/** + * @author Steffen Pingel + */ +public class RepositoryLocation extends PlatformObject { + + private static final String AUTH_HTTP = "org.eclipse.mylyn.tasklist.repositories.httpauth"; //$NON-NLS-1$ + + private static final String AUTH_PROXY = "org.eclipse.mylyn.tasklist.repositories.proxy"; //$NON-NLS-1$ + + private static final String AUTH_REPOSITORY = "org.eclipse.mylyn.tasklist.repositories"; //$NON-NLS-1$ + + private static final String ENABLED = ".enabled"; //$NON-NLS-1$ + + private static final String ID_PLUGIN = "org.eclipse.mylyn.commons.repository"; //$NON-NLS-1$ + + public static final String PROPERTY_CATEGORY = "category"; //$NON-NLS-1$ + + public static final String PROPERTY_ENCODING = "encoding"; //$NON-NLS-1$ + + public static final String PROPERTY_ID = "id"; //$NON-NLS-1$ + + public static final String PROPERTY_LABEL = "label"; //$NON-NLS-1$ + + public static final String PROPERTY_OFFLINE = "org.eclipse.mylyn.tasklist.repositories.offline"; //$NON-NLS-1$ + + public static final String PROPERTY_TIMEZONE = "timezone"; //$NON-NLS-1$ + + public static final String PROPERTY_URL = "url"; //$NON-NLS-1$ + + public static final String PROPERTY_USERNAME = "org.eclipse.mylyn.repositories.username"; //$NON-NLS-1$ + + public static final String PROPERTY_PROXY_HOST = "org.eclipse.mylyn.repositories.proxy.host"; //$NON-NLS-1$ + + public static final String PROPERTY_PROXY_PORT = "org.eclipse.mylyn.repositories.proxy.port"; //$NON-NLS-1$ + + public static final String PROPERTY_PROXY_USEDEFAULT = "org.eclipse.mylyn.repositories.proxy.usedefault"; //$NON-NLS-1$ + + private static final String SAVE_PASSWORD = ".savePassword"; //$NON-NLS-1$ + + private static final String USERNAME = ".username"; //$NON-NLS-1$ + + private static String getKeyPrefix(AuthenticationType type) { + switch (type) { + case HTTP: + return AUTH_HTTP; + case PROXY: + return AUTH_PROXY; + case REPOSITORY: + return AUTH_REPOSITORY; + } + throw new IllegalArgumentException("Unknown authentication type: " + type); //$NON-NLS-1$ + } + + private String cachedUserName; + + private ICredentialsStore credentialsStore; + + // transient + private IStatus errorStatus = null; + + private boolean isCachedUserName; + + private final Map<String, String> properties = new LinkedHashMap<String, String>(); + + private final Set<PropertyChangeListener> propertyChangeListeners = new HashSet<PropertyChangeListener>(); + + private ILocationService service; + + private boolean workingCopy; + + public RepositoryLocation() { + this.service = LocationService.getDefault(); + } + + public RepositoryLocation(Map<String, String> properties) { + this.properties.putAll(properties); + this.workingCopy = true; + this.service = LocationService.getDefault(); + } + + public RepositoryLocation(RepositoryLocation source) { + this.properties.putAll(source.properties); + this.workingCopy = true; + this.service = source.getService(); + } + + public void addChangeListener(PropertyChangeListener listener) { + propertyChangeListeners.add(listener); + } + + public void clearCredentials() { + getCredentialsStore().clear(); + } + + public <T extends AuthenticationCredentials> T getCredentials(AuthenticationType authType, Class<T> credentialsKind) { + String prefix = getKeyPrefix(authType); + if (getBooleanPropery(prefix + ENABLED)) { + if (getId() == null) { + // can't determine location of credentials + return null; + } + try { + return CredentialsFactory.create(credentialsKind, getCredentialsStore(), prefix); + } catch (StorageException e) { + // FIXME + } + } + return null; + } + + public ICredentialsStore getCredentialsStore() { + if (credentialsStore == null) { + return getService().getCredentialsStore(getId()); + } + return credentialsStore; + } + + public String getId() { + String id = getProperty(PROPERTY_ID); + if (id == null) { + throw new IllegalStateException("Repository ID is not set"); //$NON-NLS-1$ + } + return id; + } + + public Map<String, String> getProperties() { + return new LinkedHashMap<String, String>(this.properties); + } + + public String getProperty(String name) { + return this.properties.get(name); + } + + /** + * @return the URL if the label property is not set + */ + public String getLabel() { + String label = properties.get(PROPERTY_LABEL); + if (label != null && label.length() > 0) { + return label; + } else { + return getUrl(); + } + } + + /** + * @since 3.0 + */ + public boolean getSavePassword(AuthenticationType authType) { + return getBooleanPropery(getKeyPrefix(authType) + SAVE_PASSWORD); + } + + public boolean getBooleanPropery(String key) { + String value = getProperty(key); + return value != null && Boolean.parseBoolean(value); + } + + public ILocationService getService() { + return service; + } + + /** + * @since 3.0 + */ + public IStatus getStatus() { + return errorStatus; + } + + public String getUrl() { + return getProperty(PROPERTY_URL); + } + + /** + * The username is cached since it needs to be retrieved frequently (e.g. for Task List decoration). + */ + public String getUserName() { + return getProperty(PROPERTY_USERNAME); + } + + public void setUserName(String userName) { + setProperty(PROPERTY_USERNAME, userName); + } + + private void handlePropertyChange(String key, Object old, Object value) { + if (PROPERTY_ID.equals(key)) { + credentialsStore = null; + } + + PropertyChangeEvent event = new PropertyChangeEvent(this, key, old, value); + for (PropertyChangeListener listener : propertyChangeListeners) { + listener.propertyChange(event); + } + } + + private boolean hasChanged(Object oldValue, Object newValue) { + return oldValue != null && !oldValue.equals(newValue) || oldValue == null && newValue != null; + } + + public boolean hasProperty(String name) { + String value = getProperty(name); + return value != null && value.trim().length() > 0; + } + + public boolean isOffline() { + return Boolean.parseBoolean(getProperty(PROPERTY_OFFLINE)); + } + + public boolean isWorkingCopy() { + return workingCopy; + } + + /** + * @since 3.0 + */ + public void removeChangeListener(PropertyChangeListener listener) { + propertyChangeListeners.remove(listener); + } + + public void removeProperty(String key) { + setProperty(key, null); + } + + public <T extends AuthenticationCredentials> void setCredentials(AuthenticationType authType, T credentials) { + String prefix = getKeyPrefix(authType); + + if (credentials == null) { + if (authType == AuthenticationType.REPOSITORY) { + cachedUserName = null; + } + setProperty(prefix + ENABLED, String.valueOf(false)); + } else { + setProperty(prefix + ENABLED, String.valueOf(true)); + try { + credentials.save(getCredentialsStore(), prefix); + } catch (StorageException e) { + // FIXME + } + } + } + + public void setCredentialsStore(ICredentialsStore credentialsStore) { + this.credentialsStore = credentialsStore; + } + + public void setLabel(String label) { + setProperty(PROPERTY_LABEL, label); + } + + public void setOffline(boolean offline) { + properties.put(PROPERTY_OFFLINE, String.valueOf(offline)); + } + + public void setProperty(String key, String newValue) { + Assert.isNotNull(key); + String oldValue = this.properties.get(key); + if (hasChanged(oldValue, newValue)) { + this.properties.put(key.intern(), (newValue != null) ? newValue.intern() : null); + handlePropertyChange(key, oldValue, newValue); + } + } + + public void setService(ILocationService service) { + this.service = service; + } + + public void setStatus(IStatus errorStatus) { + this.errorStatus = errorStatus; + } + + @Override + public String toString() { + return getLabel(); + } + + public void apply(RepositoryLocation location) { + String oldId = getProperty(PROPERTY_ID); + ICredentialsStore oldCredentialsStore = null; + if (oldId != null) { + oldCredentialsStore = getCredentialsStore(); + } + + // merge properties + HashSet<String> removed = new HashSet<String>(properties.keySet()); + removed.removeAll(location.properties.keySet()); + for (Map.Entry<String, String> entry : location.properties.entrySet()) { + setProperty(entry.getKey(), entry.getValue()); + } + for (String key : removed) { + setProperty(key, null); + } + + String newId = getProperty(PROPERTY_ID); + if (newId != null) { + // migrate credentials if url has changed + ICredentialsStore newCredentialsStore = getCredentialsStore(); + if (!newId.equals(oldId)) { + if (oldCredentialsStore != null) { + try { + oldCredentialsStore.copyTo(newCredentialsStore); + oldCredentialsStore.clear(); + } catch (StorageException e) { + // FIXME + } + } + } + + // merge credentials + if (location.getCredentialsStore() instanceof InMemoryCredentialsStore) { + try { + ((InMemoryCredentialsStore) location.getCredentialsStore()).copyTo(newCredentialsStore); + } catch (StorageException e) { + // FIXME + } + } + } + + } + + public void setIdPreservingCredentialsStore(String id) { + ICredentialsStore store = getCredentialsStore(); + setProperty(RepositoryLocation.PROPERTY_ID, id); + if (this.credentialsStore == null) { + setCredentialsStore(store); + } + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryValidator.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryValidator.java new file mode 100644 index 0000000..905caaa --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/RepositoryValidator.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; + +/** + * @author Steffen Pingel + */ +public abstract class RepositoryValidator { + + private final RepositoryLocation location; + + private IStatus result; + + public RepositoryValidator(RepositoryLocation location) { + this.location = location; + } + + public RepositoryLocation getLocation() { + return location; + } + + public IStatus getResult() { + return result; + } + + public abstract IStatus run(IProgressMonitor monitor); + + public void setResult(IStatus result) { + this.result = result; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/AuthenticationCredentials.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/AuthenticationCredentials.java new file mode 100644 index 0000000..d2ab0a4 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/AuthenticationCredentials.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories.auth; + +import org.eclipse.equinox.security.storage.StorageException; + +/** + * @author Steffen Pingel + */ +public abstract class AuthenticationCredentials { + + public abstract void save(ICredentialsStore store, String prefix) throws StorageException; + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/AuthenticationType.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/AuthenticationType.java new file mode 100644 index 0000000..05d3330 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/AuthenticationType.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories.auth; + +/** + * An enum of supported authentication types. + * + * @author Steffen Pingel + */ +public enum AuthenticationType { + /** + * HTTP authentication, this is typically basic authentication but other methods such as digest or NTLM are used as + * well. + */ + HTTP, + /** Proxy authentication. */ + PROXY, + /** Task repository authentication. */ + REPOSITORY +}
\ No newline at end of file diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/ICredentialsStore.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/ICredentialsStore.java new file mode 100644 index 0000000..05962ff --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/ICredentialsStore.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories.auth; + +import java.io.IOException; + +import org.eclipse.equinox.security.storage.StorageException; + +/** + * @author Steffen Pingel + * @noextend This interface is not intended to be extended by clients. + * @noimplement This interface is not intended to be implemented by clients. + */ +public interface ICredentialsStore { + + public void clear(); + + public void flush() throws IOException; + + public String get(String key, String def) throws StorageException; + + public byte[] getByteArray(String key, byte[] def) throws StorageException; + + public String[] keys(); + + public void put(String key, String value, boolean encrypt) throws StorageException; + + public void putByteArray(String key, byte[] value, boolean encrypt) throws StorageException; + + public void remove(String key); + + public void copyTo(ICredentialsStore target) throws StorageException; + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/UsernamePasswordCredentials.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/UsernamePasswordCredentials.java new file mode 100644 index 0000000..27b85bf --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/commons/repositories/auth/UsernamePasswordCredentials.java @@ -0,0 +1,105 @@ +/******************************************************************************* + * Copyright (c) 2004, 2008 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.repositories.auth; + +import org.eclipse.equinox.security.storage.StorageException; + +/** + * Provides a user name and password. + * + * @author Steffen Pingel + * @since 2.2 + * @noextend This class is not intended to be subclassed by clients. + */ +public class UsernamePasswordCredentials extends AuthenticationCredentials { + + private final String userName; + + private final String password; + + /** + * @param userName + * the user name, must not be null + * @param password + * the password, must not be null + */ + public UsernamePasswordCredentials(String userName, String password) { + if (userName == null) { + throw new IllegalArgumentException(); + } + if (password == null) { + throw new IllegalArgumentException(); + } + + this.userName = userName; + this.password = password; + } + + public String getUserName() { + return userName; + } + + public String getPassword() { + return password; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((password == null) ? 0 : password.hashCode()); + result = prime * result + ((userName == null) ? 0 : userName.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final UsernamePasswordCredentials other = (UsernamePasswordCredentials) obj; + if (password == null) { + if (other.password != null) { + return false; + } + } else if (!password.equals(other.password)) { + return false; + } + if (userName == null) { + if (other.userName != null) { + return false; + } + } else if (!userName.equals(other.userName)) { + return false; + } + return true; + } + + public static UsernamePasswordCredentials create(ICredentialsStore store, String prefix) throws StorageException { + String userName = store.get(prefix + ".user", ""); //$NON-NLS-1$ //$NON-NLS-2$ + String password = store.get(prefix + ".password", ""); //$NON-NLS-1$ //$NON-NLS-2$ + return new UsernamePasswordCredentials(userName, password); + } + + @Override + public void save(ICredentialsStore store, String prefix) throws StorageException { + store.put(prefix + ".user", userName, false); //$NON-NLS-1$ + store.put(prefix + ".password", password, true); //$NON-NLS-1$ + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/CredentialsFactory.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/CredentialsFactory.java new file mode 100644 index 0000000..7412b63 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/CredentialsFactory.java @@ -0,0 +1,34 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.repositories; + +import org.eclipse.equinox.security.storage.StorageException; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationCredentials; +import org.eclipse.mylyn.commons.repositories.auth.ICredentialsStore; +import org.eclipse.mylyn.commons.repositories.auth.UsernamePasswordCredentials; + +/** + * Simple factory that creates {@link AuthenticationCredentials} objects. + * + * @author Steffen Pingel + */ +public class CredentialsFactory { + + public static <T extends AuthenticationCredentials> T create(Class<T> credentialsKind, + ICredentialsStore credentialsStore, String key) throws StorageException { + if (credentialsKind == UsernamePasswordCredentials.class) { + return (T) UsernamePasswordCredentials.create(credentialsStore, key); + } + throw new IllegalArgumentException("Unknown credentials type: " + credentialsKind); //$NON-NLS-1$ + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/InMemoryCredentialsStore.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/InMemoryCredentialsStore.java new file mode 100644 index 0000000..69c692e --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/InMemoryCredentialsStore.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.repositories; + +import java.io.IOException; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.equinox.security.storage.StorageException; +import org.eclipse.mylyn.commons.repositories.auth.ICredentialsStore; + +/** + * @author Steffen Pingel + */ +public class InMemoryCredentialsStore implements ICredentialsStore { + + private final ConcurrentHashMap<String, Object> store; + + private final ICredentialsStore parent; + + public InMemoryCredentialsStore(ICredentialsStore parent) { + this.parent = parent; + store = new ConcurrentHashMap<String, Object>(); + } + + public void clear() { + store.clear(); + } + + public void flush() throws IOException { + // does nothing + } + + public String get(String key, String def) throws StorageException { + String value = (String) store.get(key); + if (value == null && parent != null) { + return parent.get(key, def); + } + return (value != null) ? value : def; + } + + public byte[] getByteArray(String key, byte[] def) throws StorageException { + byte[] value = (byte[]) store.get(key); + if (value == null && parent != null) { + return parent.getByteArray(key, def); + } + return (value != null) ? value : def; + } + + public String[] keys() { + return store.keySet().toArray(new String[0]); + } + + public void put(String key, String value, boolean encrypt) throws StorageException { + store.put(key, value); + } + + public void putByteArray(String key, byte[] value, boolean encrypt) throws StorageException { + store.put(key, value); + } + + public void remove(String key) { + store.remove(key); + } + + public void copyTo(ICredentialsStore target) throws StorageException { + for (Map.Entry<String, Object> entry : store.entrySet()) { + if (entry.getValue() instanceof String) { + target.put(entry.getKey(), (String) entry.getValue(), true); + } else if (entry.getValue() instanceof byte[]) { + target.putByteArray(entry.getKey(), (byte[]) entry.getValue(), true); + } + } + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/LocationService.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/LocationService.java new file mode 100644 index 0000000..b6bc8f9 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/LocationService.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.repositories; + +import java.net.Proxy; +import java.util.HashMap; +import java.util.Map; + +import javax.net.ssl.X509TrustManager; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.mylyn.commons.net.IProxyProvider; +import org.eclipse.mylyn.commons.net.WebUtil; +import org.eclipse.mylyn.commons.repositories.ILocationService; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationCredentials; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationType; +import org.eclipse.mylyn.commons.repositories.auth.ICredentialsStore; +import org.eclipse.mylyn.commons.repositories.auth.UsernamePasswordCredentials; + +/** + * @author Steffen Pingel + */ +public class LocationService implements ILocationService { + + public static final String ID_PLUGIN = "org.eclipse.mylyn.commons.repository"; //$NON-NLS-1$ + + private static LocationService instance = new LocationService(null, null, new PlatformProxyProvider()); + + public static LocationService getDefault() { + return instance; + } + + private static class PlatformProxyProvider implements IProxyProvider { + + public Proxy getProxyForHost(String host, String proxyType) { + return WebUtil.getProxy(host, proxyType); + } + + } + + private final Map<AuthenticationType, UsernamePasswordCredentials> credentialsByType; + + private final IProxyProvider proxyProvider; + + public LocationService(String username, String password, IProxyProvider proxyProvider) { + this.credentialsByType = new HashMap<AuthenticationType, UsernamePasswordCredentials>(); + this.proxyProvider = proxyProvider; + + if (username != null && password != null) { + setCredentials(AuthenticationType.REPOSITORY, username, password); + } + } + +// public LocationService(String url, String username, String password) { +// this(url, username, password, new PlatformProxyProvider()); +// } +// +// public LocationService(String url) { +// this(url, null, null, new PlatformProxyProvider()); +// } + + public UsernamePasswordCredentials getCredentials(AuthenticationType authType) { + return credentialsByType.get(authType); + } + + public Proxy getProxyForHost(String host, String proxyType) { + if (proxyProvider != null) { + return proxyProvider.getProxyForHost(host, proxyType); + } + return null; + } + + public void setCredentials(AuthenticationType authType, String username, String password) { + credentialsByType.put(authType, new UsernamePasswordCredentials(username, password)); + } + + public X509TrustManager getTrustManager() { + // ignore + return null; + } + + public <T extends AuthenticationCredentials> T requestCredentials(AuthenticationType type, + Class<T> credentialsKind, String message, IProgressMonitor monitor) { + throw new UnsupportedOperationException(); + } + + public ICredentialsStore getCredentialsStore(String id) { + return new SecureCredentialsStore(id); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/SecureCredentialsStore.java b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/SecureCredentialsStore.java new file mode 100644 index 0000000..8b8d4ae --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.repositories/src/org/eclipse/mylyn/internal/commons/repositories/SecureCredentialsStore.java @@ -0,0 +1,85 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.repositories; + +import java.io.IOException; + +import org.eclipse.equinox.security.storage.EncodingUtils; +import org.eclipse.equinox.security.storage.ISecurePreferences; +import org.eclipse.equinox.security.storage.SecurePreferencesFactory; +import org.eclipse.equinox.security.storage.StorageException; +import org.eclipse.mylyn.commons.repositories.auth.ICredentialsStore; + +/** + * @author Steffen Pingel + */ +public class SecureCredentialsStore implements ICredentialsStore { + + private static final String ID_PLUGIN = "org.eclipse.mylyn.commons.repository"; //$NON-NLS-1$ + + private final String url; + + public SecureCredentialsStore(String url) { + this.url = url; + } + + public void clear() { + //getSecurePreferences().clear(); + getSecurePreferences().removeNode(); + } + + public void flush() throws IOException { + getSecurePreferences().flush(); + } + + public String get(String key, String def) throws StorageException { + return getSecurePreferences().get(key, def); + } + + public byte[] getByteArray(String key, byte[] def) throws StorageException { + return getSecurePreferences().getByteArray(key, def); + } + + private ISecurePreferences getSecurePreferences() { + ISecurePreferences securePreferences = SecurePreferencesFactory.getDefault().node(ID_PLUGIN); + securePreferences = securePreferences.node(EncodingUtils.encodeSlashes(getUrl())); + return securePreferences; + } + + public String getUrl() { + return url; + } + + public String[] keys() { + return getSecurePreferences().keys(); + } + + public void put(String key, String value, boolean encrypt) throws StorageException { + getSecurePreferences().put(key, value, encrypt); + } + + public void putByteArray(String key, byte[] value, boolean encrypt) throws StorageException { + getSecurePreferences().putByteArray(key, value, encrypt); + } + + public void remove(String key) { + getSecurePreferences().remove(key); + } + + public void copyTo(ICredentialsStore target) throws StorageException { + ISecurePreferences preferences = getSecurePreferences(); + for (String key : preferences.keys()) { + target.put(key, preferences.get(key, null), preferences.isEncrypted(key)); + } + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/.project b/stubs/org.eclipse.mylyn.commons.team/.project index fea2210..66ea0a7 100644 --- a/stubs/org.eclipse.mylyn.commons.team/.project +++ b/stubs/org.eclipse.mylyn.commons.team/.project @@ -1,6 +1,6 @@ <?xml version="1.0" encoding="UTF-8"?> <projectDescription> - <name>org.eclipse.mylyn.commons.team</name> + <name>org.eclipse.mylyn.commons.team-stub</name> <comment></comment> <projects> </projects> diff --git a/stubs/org.eclipse.mylyn.commons.team/META-INF/MANIFEST.MF b/stubs/org.eclipse.mylyn.commons.team/META-INF/MANIFEST.MF index 6e6bcf6..9ceca73 100644 --- a/stubs/org.eclipse.mylyn.commons.team/META-INF/MANIFEST.MF +++ b/stubs/org.eclipse.mylyn.commons.team/META-INF/MANIFEST.MF @@ -2,8 +2,23 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-SymbolicName: org.eclipse.mylyn.commons.team;singleton:=true -Bundle-Version: 0.9.0.v20111206-0100 +Bundle-Version: 0.9.0.v20120225-0100 Bundle-Vendor: %Bundle-Vendor Bundle-RequiredExecutionEnvironment: J2SE-1.5 Bundle-ClassPath: . Bundle-Localization: plugin +Require-Bundle: org.eclipse.ui, + org.eclipse.core.runtime, + org.eclipse.ui.forms, + org.eclipse.ui.navigator, + org.eclipse.ui.navigator.resources, + org.eclipse.core.databinding, + org.eclipse.core.databinding.beans, + org.eclipse.core.databinding.property;resolution:=optional, + org.eclipse.jface.databinding, + org.eclipse.mylyn.commons.repositories;bundle-version="0.1.0", + org.eclipse.mylyn.commons.ui;bundle-version="3.5.0" +Bundle-ActivationPolicy: lazy +Export-Package: org.eclipse.mylyn.commons.ui.team;x-internal:=true, + org.eclipse.mylyn.internal.commons.ui.team;x-internal:=true, + org.eclipse.mylyn.internal.commons.ui.team.wizards;x-internal:=true diff --git a/stubs/org.eclipse.mylyn.commons.team/pom.xml b/stubs/org.eclipse.mylyn.commons.team/pom.xml index 1cf415a..14af928 100644 --- a/stubs/org.eclipse.mylyn.commons.team/pom.xml +++ b/stubs/org.eclipse.mylyn.commons.team/pom.xml @@ -8,7 +8,7 @@ <version>3.7.0-SNAPSHOT</version> </parent> <artifactId>org.eclipse.mylyn.commons.team</artifactId> - <version>0.9.0.v20111206-0100</version> + <version>0.9.0.v20120225-0100</version> <packaging>eclipse-plugin</packaging> <build> <plugins> diff --git a/stubs/org.eclipse.mylyn.commons.team/src/.placehoder b/stubs/org.eclipse.mylyn.commons.team/src/.placehoder deleted file mode 100644 index e69de29..0000000 --- a/stubs/org.eclipse.mylyn.commons.team/src/.placehoder +++ b/dev/null diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/IPartContainer.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/IPartContainer.java new file mode 100644 index 0000000..9f2f7e4 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/IPartContainer.java @@ -0,0 +1,27 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.team; + +import org.eclipse.jface.operation.IRunnableContext; + +/** + * @author Steffen Pingel + * @noimplement This interface is not intended to be implemented by clients. + * @noextend This interface is not intended to be extended by clients. + */ +public interface IPartContainer extends IRunnableContext { + + public void setMessage(String message, int messageType); + + public void updateButtons(); + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/Messages.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/Messages.java new file mode 100644 index 0000000..c71361e --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/Messages.java @@ -0,0 +1,66 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.team; + +import org.eclipse.osgi.util.NLS; + +class Messages extends NLS { + + private static final String BUNDLE_NAME = "org.eclipse.mylyn.commons.ui.team.messages"; //$NON-NLS-1$ + + public static String RepositoryLocationPart_Anonymous; + + public static String RepositoryLocationPart_Change_Settings; + + public static String RepositoryLocationPart_Disconnected; + + public static String RepositoryLocationPart_Enable_HTTP_Authentication; + + public static String RepositoryLocationPart_Enable_Proxy_Authentication; + + public static String RepositoryLocationPart_Enter_a_valid_server_url; + + public static String RepositoryLocationPart_HTTP_Authentication; + + public static String RepositoryLocationPart_Label; + + public static String RepositoryLocationPart_Password; + + public static String RepositoryLocationPart_Proxy_Host; + + public static String RepositoryLocationPart_Proxy_Port; + + public static String RepositoryLocationPart_Proxy_Server_Configuration; + + public static String RepositoryLocationPart_Repository_is_valid; + + public static String RepositoryLocationPart_Save_Password; + + public static String RepositoryLocationPart_Server; + + public static String RepositoryLocationPart_Unexpected_error_during_repository_validation; + + public static String RepositoryLocationPart_Use_global_Network_Connections_preferences; + + public static String RepositoryLocationPart_User; + + public static String RepositoryLocationPart_Validating_repository; + + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryLocationPart.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryLocationPart.java new file mode 100644 index 0000000..ae2e89f --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryLocationPart.java @@ -0,0 +1,613 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.team; + +import java.lang.reflect.InvocationTargetException; +import java.net.URI; +import java.util.concurrent.atomic.AtomicReference; + +import org.eclipse.core.databinding.DataBindingContext; +import org.eclipse.core.databinding.UpdateValueStrategy; +import org.eclipse.core.databinding.observable.value.IObservableValue; +import org.eclipse.core.databinding.validation.IValidator; +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.OperationCanceledException; +import org.eclipse.core.runtime.Status; +import org.eclipse.jface.databinding.dialog.DialogPageSupport; +import org.eclipse.jface.databinding.swt.ISWTObservableValue; +import org.eclipse.jface.databinding.swt.SWTObservables; +import org.eclipse.jface.databinding.wizard.WizardPageSupport; +import org.eclipse.jface.dialogs.DialogPage; +import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.preference.PreferenceDialog; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.mylyn.commons.repositories.RepositoryLocation; +import org.eclipse.mylyn.commons.repositories.RepositoryValidator; +import org.eclipse.mylyn.commons.repositories.auth.AuthenticationType; +import org.eclipse.mylyn.commons.repositories.auth.UsernamePasswordCredentials; +import org.eclipse.mylyn.internal.commons.ui.SectionComposite; +import org.eclipse.mylyn.internal.commons.ui.team.RepositoryLocationValueProperty; +import org.eclipse.mylyn.internal.commons.ui.team.TeamUiPlugin; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ModifyEvent; +import org.eclipse.swt.events.ModifyListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +import org.eclipse.swt.events.SelectionListener; +import org.eclipse.swt.widgets.Button; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.swt.widgets.Link; +import org.eclipse.swt.widgets.Text; +import org.eclipse.ui.dialogs.PreferencesUtil; +import org.eclipse.ui.forms.widgets.ExpandableComposite; +import org.eclipse.ui.statushandlers.StatusManager; + +/** + * @author Steffen Pingel + * @since 3.5 + */ +public class RepositoryLocationPart { + + public class UrlValidator implements IValidator { + + public IStatus validate(Object value) { + if (!isValidUrl(value.toString())) { + return new Status(IStatus.ERROR, TeamUiPlugin.ID_PLUGIN, + Messages.RepositoryLocationPart_Enter_a_valid_server_url); + } + return Status.OK_STATUS; + } + + } + + private class UsernamePasswordListener implements ModifyListener, SelectionListener { + + private final AuthenticationType authenticationType; + + private final Button enabledButton; + + private boolean enablementReversed; + + private final Text passwordText; + + private final Button savePasswordButton; + + private boolean updating; + + private final Text userText; + + public UsernamePasswordListener(AuthenticationType authenticationType, Button enabledButton, Text userText, + Text passwordText, Button savePasswordButton) { + this.authenticationType = authenticationType; + this.enabledButton = enabledButton; + this.userText = userText; + this.passwordText = passwordText; + this.savePasswordButton = savePasswordButton; + init(); + } + + private void apply() { + if (updating) { + return; + } + if (isEnabled()) { + UsernamePasswordCredentials credentials = new UsernamePasswordCredentials(userText.getText(), + passwordText.getText()); + getWorkingCopy().setCredentials(authenticationType, credentials); + } else { + getWorkingCopy().setCredentials(authenticationType, null); + } + } + + protected void init() { + enabledButton.addSelectionListener(this); + userText.addModifyListener(this); + passwordText.addModifyListener(this); + savePasswordButton.addSelectionListener(this); + } + + protected boolean isEnabled() { + return enabledButton.getSelection() != isEnablementReversed(); + } + + public boolean isEnablementReversed() { + return enablementReversed; + } + + public void modifyText(ModifyEvent event) { + apply(); + } + + private void restore() { + try { + updating = true; + UsernamePasswordCredentials credentials = getWorkingCopy().getCredentials(authenticationType, + UsernamePasswordCredentials.class); + if (credentials != null) { + enabledButton.setSelection(!isEnablementReversed()); + userText.setText(credentials.getUserName()); + passwordText.setText(credentials.getUserName()); + savePasswordButton.setSelection(true); + } else { + enabledButton.setSelection(isEnablementReversed()); + userText.setText(""); //$NON-NLS-1$ + passwordText.setText(""); //$NON-NLS-1$ + savePasswordButton.setSelection(true); + } + } finally { + updating = false; + } + updateWidgetEnablement(); + } + + public void setEnablementReversed(boolean enablementReversed) { + this.enablementReversed = enablementReversed; + } + + private void updateWidgetEnablement() { + boolean enabled = isEnabled(); + userText.setEnabled(enabled); + passwordText.setEnabled(enabled); + savePasswordButton.setEnabled(enabled); + } + + public void widgetDefaultSelected(SelectionEvent event) { + apply(); + + } + + public void widgetSelected(SelectionEvent event) { + apply(); + if (event.widget == enabledButton) { + updateWidgetEnablement(); + } + } + + } + + protected static final String PREFS_PAGE_ID_NET_PROXY = "org.eclipse.ui.net.NetPreferences"; //$NON-NLS-1$ + + private DataBindingContext bindingContext; + + private boolean needsAdditionalSections; + + private boolean needsAnonymousLogin; + + private boolean needsHttpAuth; + + private boolean needsProxy; + + private boolean needsValidation; + + private IAdaptable serviceLocator; + + private final RepositoryLocation workingCopy; + + public RepositoryLocationPart(RepositoryLocation workingCopy) { + this.workingCopy = workingCopy; + setNeedsProxy(false); + setNeedsHttpAuth(false); + setNeedsValidation(true); + } + + protected void applyValidatorResult(RepositoryValidator validator) { + IStatus status = validator.getResult(); + String message = status.getMessage(); + if (message == null || message.length() == 0) { + message = null; + } + switch (status.getSeverity()) { + case IStatus.OK: + if (status == Status.OK_STATUS) { +// if (getUserName().length() > 0) { +// message = "Credentials are valid."; +// } else { + message = Messages.RepositoryLocationPart_Repository_is_valid; +// } + } + getPartContainer().setMessage(message, IMessageProvider.INFORMATION); + break; + case IStatus.INFO: + getPartContainer().setMessage(message, IMessageProvider.INFORMATION); + break; + case IStatus.WARNING: + getPartContainer().setMessage(message, IMessageProvider.WARNING); + break; + default: + getPartContainer().setMessage(message, IMessageProvider.ERROR); + break; + } + } + + private void bind(AuthenticationType authType, Button anonymousButton, Text userText, Text passwordText, + Button savePasswordButton, boolean reverseEnablement) { + UsernamePasswordListener listener = new UsernamePasswordListener(authType, anonymousButton, userText, + passwordText, savePasswordButton); + listener.setEnablementReversed(reverseEnablement); + listener.restore(); + } + + protected void bind(Button button, String property) { + ISWTObservableValue uiElement = SWTObservables.observeSelection(button); + IObservableValue modelElement = new RepositoryLocationValueProperty(property, Boolean.FALSE.toString()).observe(workingCopy); + bindingContext.bindValue(uiElement, modelElement, null, null); + } + + protected void bind(Text text, String property) { + bind(text, property, null, null); + } + + protected void bind(Text text, String property, UpdateValueStrategy targetObservableValue, + UpdateValueStrategy modelObservableValue) { + ISWTObservableValue uiElement = SWTObservables.observeText(text, SWT.Modify); + IObservableValue modelElement = new RepositoryLocationValueProperty(property, null).observe(workingCopy); + bindingContext.bindValue(uiElement, modelElement, targetObservableValue, modelObservableValue); + } + + /** + * Returns whether this page can be validated or not. + * <p> + * This information is typically used by the wizard to set the enablement of the validation UI affordance. + * </p> + * + * @return <code>true</code> if this page can be validated, and <code>false</code> otherwise + * @see #needsValidation() + * @see IWizardContainer#updateButtons() + */ + public boolean canValidate() { + return getValidator() != null; + } + + protected Control createAdditionalContents(Composite composite) { + return null; + } + + public Control createContents(Composite parent) { + bindingContext = new DataBindingContext(); + WizardPage wizardPage = getContainer(WizardPage.class); + if (wizardPage != null) { + WizardPageSupport.create(wizardPage, bindingContext); + } else { + DialogPage page = getContainer(DialogPage.class); + if (page != null) { + DialogPageSupport.create(page, bindingContext); + } + } + Composite composite = new Composite(parent, SWT.NONE); + GridLayoutFactory.swtDefaults().numColumns(3).applyTo(composite); + +// Composite this = new Composite(parent, SWT.NULL); +// Layout layout = new FillLayout(); +// this.setLayout(layout); + + createServerSection(composite); + createUserSection(composite); + + Control control = createAdditionalContents(composite); + if (control != null) { + GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(control); + } + + if (needsHttpAuth() || needsProxy() || needsAdditionalSections()) { + SectionComposite sectionComposite = new SectionComposite(composite, SWT.NONE); + GridDataFactory.fillDefaults().grab(true, true).span(3, 1).applyTo(sectionComposite); + + if (needsHttpAuth()) { + createHttpAuthSection(sectionComposite); + } + if (needsProxy()) { + createProxySection(sectionComposite); + } + createSections(sectionComposite); + } + +// Button validateButton = new Button(composite, SWT.PUSH); +// validateButton.setText("Validate"); +// validateButton.addSelectionListener(new SelectionAdapter() { +// @Override +// public void widgetSelected(SelectionEvent e) { +// validate(); +// } +// }); + + return composite; + } + + private void createHttpAuthSection(SectionComposite parent) { + int style = SWT.NONE; + if (getWorkingCopy().getCredentials(AuthenticationType.HTTP, UsernamePasswordCredentials.class) != null) { + style |= ExpandableComposite.EXPANDED; + } + ExpandableComposite section = parent.createSection(Messages.RepositoryLocationPart_HTTP_Authentication, style); + section.clientVerticalSpacing = 5; + + Composite composite = new Composite(section, SWT.NONE); + section.setClient(composite); + GridLayoutFactory.fillDefaults().numColumns(3).applyTo(composite); + + Label label; + + Button enableButton = new Button(composite, SWT.CHECK); + GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(enableButton); + enableButton.setText(Messages.RepositoryLocationPart_Enable_HTTP_Authentication); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_User); + + Text userText = new Text(composite, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(userText); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_Password); + + Text passwordText = new Text(composite, SWT.BORDER | SWT.PASSWORD); + GridDataFactory.fillDefaults().grab(true, false).applyTo(passwordText); + + Button savePasswordButton = new Button(composite, SWT.CHECK); + savePasswordButton.setText(Messages.RepositoryLocationPart_Save_Password); + + bind(AuthenticationType.HTTP, enableButton, userText, passwordText, savePasswordButton, false); + } + + private void createProxySection(final SectionComposite parent) { + ExpandableComposite section = parent.createSection(Messages.RepositoryLocationPart_Proxy_Server_Configuration); + + Composite composite = new Composite(section, SWT.NONE); + section.setClient(composite); + GridLayoutFactory.fillDefaults().numColumns(3).applyTo(composite); + + Label label; + + Button systemProxyButton = new Button(composite, SWT.CHECK); + GridDataFactory.fillDefaults().span(2, SWT.DEFAULT).applyTo(systemProxyButton); + systemProxyButton.setText(Messages.RepositoryLocationPart_Use_global_Network_Connections_preferences); +// systemProxyButton.addSelectionListener(new SelectionAdapter() { +// @Override +// public void widgetSelected(SelectionEvent e) { +// updateProxyEnablement(systemProxyButton.getSelection()); +// } +// }); + bind(systemProxyButton, RepositoryLocation.PROPERTY_PROXY_USEDEFAULT); + + Link changeProxySettingsLink = new Link(composite, SWT.NONE); + changeProxySettingsLink.setText(Messages.RepositoryLocationPart_Change_Settings); + changeProxySettingsLink.addSelectionListener(new SelectionAdapter() { + @Override + public void widgetSelected(SelectionEvent e) { + PreferenceDialog dlg = PreferencesUtil.createPreferenceDialogOn(parent.getShell(), + PREFS_PAGE_ID_NET_PROXY, new String[] { PREFS_PAGE_ID_NET_PROXY }, null); + dlg.open(); + } + }); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_Proxy_Host); + + Text proxyHostText = new Text(composite, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(proxyHostText); + bind(proxyHostText, RepositoryLocation.PROPERTY_PROXY_HOST); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_Proxy_Port); + + Text proxyPortText = new Text(composite, SWT.BORDER | SWT.PASSWORD); + GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(proxyPortText); + bind(proxyPortText, RepositoryLocation.PROPERTY_PROXY_PORT); + + // authentication + + Button enableButton = new Button(composite, SWT.CHECK); + GridDataFactory.fillDefaults().grab(true, false).span(3, 1).applyTo(enableButton); + enableButton.setText(Messages.RepositoryLocationPart_Enable_Proxy_Authentication); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_User); + + Text userText = new Text(composite, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(userText); + + label = new Label(composite, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_Password); + + Text passwordText = new Text(composite, SWT.BORDER | SWT.PASSWORD); + GridDataFactory.fillDefaults().grab(true, false).applyTo(passwordText); + + Button savePasswordButton = new Button(composite, SWT.CHECK); + savePasswordButton.setText(Messages.RepositoryLocationPart_Save_Password); + + bind(AuthenticationType.PROXY, enableButton, userText, passwordText, savePasswordButton, false); + } + + protected void createSections(SectionComposite sectionComposite) { + } + + private void createServerSection(Composite parent) { + Label label; + + label = new Label(parent, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_Server); + + Text urlText = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().span(2, 1).grab(true, false).applyTo(urlText); + bind(urlText, RepositoryLocation.PROPERTY_URL, getUrlUpdateValueStrategy(), null); + + label = new Label(parent, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_Label); + + Text labelText = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(labelText); + bind(labelText, RepositoryLocation.PROPERTY_LABEL); + + Button disconnectedButton = new Button(parent, SWT.CHECK); + disconnectedButton.setText(Messages.RepositoryLocationPart_Disconnected); + bind(disconnectedButton, RepositoryLocation.PROPERTY_OFFLINE); + } + + protected UpdateValueStrategy getUrlUpdateValueStrategy() { + return new UpdateValueStrategy().setAfterConvertValidator(new UrlValidator()); + } + + private void createUserSection(Composite parent) { + Label label; + + label = new Label(parent, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_User); + + Text userText = new Text(parent, SWT.BORDER); + GridDataFactory.fillDefaults().grab(true, false).applyTo(userText); + bind(userText, RepositoryLocation.PROPERTY_USERNAME); + + Button anonymousButton = new Button(parent, SWT.CHECK); + anonymousButton.setText(Messages.RepositoryLocationPart_Anonymous); + + label = new Label(parent, SWT.NONE); + label.setText(Messages.RepositoryLocationPart_Password); + + Text passwordText = new Text(parent, SWT.BORDER | SWT.PASSWORD); + GridDataFactory.fillDefaults().grab(true, false).applyTo(passwordText); + + Button savePasswordButton = new Button(parent, SWT.CHECK); + savePasswordButton.setText(Messages.RepositoryLocationPart_Save_Password); + + bind(AuthenticationType.REPOSITORY, anonymousButton, userText, passwordText, savePasswordButton, true); + } + + public <T> T getContainer(Class<T> clazz) { + return (T) getServiceLocator().getAdapter(clazz); + } + + public IPartContainer getPartContainer() { + return getContainer(IPartContainer.class); + } + + private IAdaptable getServiceLocator() { + return serviceLocator; + } + + protected RepositoryValidator getValidator() { + return null; + } + + protected RepositoryLocation getWorkingCopy() { + return workingCopy; + } + + public boolean isValidUrl(String url) { + if (url.startsWith("https://") || url.startsWith("http://")) { //$NON-NLS-1$//$NON-NLS-2$ + try { + new URI(url); + return true; + } catch (Exception e) { + // fall through + } + } + return false; + } + + public boolean needsAdditionalSections() { + return needsAdditionalSections; + } + + public boolean needsAnonymousLogin() { + return needsAnonymousLogin; + } + + public boolean needsHttpAuth() { + return this.needsHttpAuth; + } + + public boolean needsProxy() { + return this.needsProxy; + } + + public boolean needsValidation() { + return needsValidation; + } + + public void setNeedsAdditionalSections(boolean needsAdditionalSections) { + this.needsAdditionalSections = needsAdditionalSections; + } + + public void setNeedsAnonymousLogin(boolean needsAnonymousLogin) { + this.needsAnonymousLogin = needsAnonymousLogin; + } + + public void setNeedsHttpAuth(boolean needsHttpAuth) { + this.needsHttpAuth = needsHttpAuth; + } + + public void setNeedsProxy(boolean needsProxy) { + this.needsProxy = needsProxy; + } + + public void setNeedsValidation(boolean needsValidation) { + this.needsValidation = needsValidation; + } + + public void setServiceLocator(IAdaptable container) { + this.serviceLocator = container; + } + + /** + * Validate settings provided by the {@link #getValidator() validator}, typically the server settings. + */ + public void validate() { + final RepositoryValidator validator = getValidator(); + if (validator == null) { + return; + } + + final AtomicReference<IStatus> result = new AtomicReference<IStatus>(); + try { + getContainer(IPartContainer.class).run(true, true, new IRunnableWithProgress() { + public void run(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException { + monitor.beginTask(Messages.RepositoryLocationPart_Validating_repository, IProgressMonitor.UNKNOWN); + try { + result.set(validator.run(monitor)); + } catch (OperationCanceledException e) { + result.set(Status.CANCEL_STATUS); + throw new InterruptedException(); + } catch (Exception e) { + throw new InvocationTargetException(e); + } finally { + monitor.done(); + } + } + }); + } catch (InvocationTargetException e) { + StatusManager.getManager().handle( + new Status(IStatus.ERROR, TeamUiPlugin.ID_PLUGIN, + Messages.RepositoryLocationPart_Unexpected_error_during_repository_validation, e), + StatusManager.SHOW | StatusManager.BLOCK | StatusManager.LOG); + return; + } catch (InterruptedException e) { + // canceled + return; + } + if (result.get() == null) { + validator.setResult(Status.OK_STATUS); + } else { + validator.setResult(result.get()); + } + getPartContainer().updateButtons(); + applyValidatorResult(validator); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryPropertyPage.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryPropertyPage.java new file mode 100644 index 0000000..36e1e87 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryPropertyPage.java @@ -0,0 +1,70 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.team; + +import java.util.UUID; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.DialogPage; +import org.eclipse.mylyn.commons.repositories.RepositoryLocation; +import org.eclipse.mylyn.internal.commons.repositories.InMemoryCredentialsStore; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.dialogs.PropertyPage; + +/** + * @author Steffen Pingel + */ +public class RepositoryPropertyPage extends PropertyPage implements IAdaptable { + + private RepositoryLocationPart part; + + private RepositoryLocation workingCopy; + + public RepositoryPropertyPage() { + } + + @Override + protected Control createContents(Composite parent) { + initializeDialogUnits(parent); + + part = new RepositoryLocationPart(getWorkingCopy()); + part.setServiceLocator(this); + setControl(part.createContents(parent)); + Dialog.applyDialogFont(parent); + return getControl(); + } + + RepositoryLocation getWorkingCopy() { + if (workingCopy == null) { + RepositoryLocation element = (RepositoryLocation) getElement().getAdapter(RepositoryLocation.class); + workingCopy = new RepositoryLocation(element); + if (workingCopy.getId() == null) { + workingCopy.setProperty(RepositoryLocation.PROPERTY_ID, UUID.randomUUID().toString()); + } + workingCopy.setCredentialsStore(new InMemoryCredentialsStore(workingCopy.getCredentialsStore())); + } + return workingCopy; + } + + public Object getAdapter(Class adapter) { + if (adapter == DialogPage.class) { + return this; + } + if (adapter == IPartContainer.class) { + return this; + } + return null; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryUi.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryUi.java new file mode 100644 index 0000000..572cdb9 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryUi.java @@ -0,0 +1,93 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.team; + +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.mylyn.internal.commons.ui.team.Messages; +import org.eclipse.mylyn.internal.commons.ui.team.TeamUiPlugin; +import org.eclipse.mylyn.internal.commons.ui.team.wizards.NewRepositoryWizard; +import org.eclipse.mylyn.internal.provisional.commons.ui.dialogs.ValidatableWizardDialog; +import org.eclipse.swt.widgets.Shell; +import org.eclipse.ui.IEditorInput; +import org.eclipse.ui.IEditorPart; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.internal.LegacyResourceSupport; +import org.eclipse.ui.internal.util.Util; + +/** + * @author Steffen Pingel + */ +public final class RepositoryUi { + + /** + * The wizard dialog width. + */ + private static final int SIZING_WIZARD_WIDTH = 500; + + /** + * The wizard dialog height. + */ + private static final int SIZING_WIZARD_HEIGHT = 500; + + public static final String ID_VIEW_REPOSITORIES = "org.eclipse.mylyn.commons.team.navigator.Repositories"; //$NON-NLS-1$ + + private RepositoryUi() { + } + + public static int openNewRepositoryDialog(IWorkbenchWindow workbenchWindow, String categoryId) { + NewRepositoryWizard wizard = new NewRepositoryWizard(); + wizard.setCategoryId(categoryId); + wizard.setWindowTitle(Messages.NewRepositoryHandler_New_Repository); + + ISelection selection = workbenchWindow.getSelectionService().getSelection(); + IStructuredSelection selectionToPass = StructuredSelection.EMPTY; + if (selection instanceof IStructuredSelection) { + selectionToPass = (IStructuredSelection) selection; + } else { + // @issue the following is resource-specific legacy code + // Build the selection from the IFile of the editor + Class resourceClass = LegacyResourceSupport.getResourceClass(); + if (resourceClass != null) { + IWorkbenchPart part = workbenchWindow.getPartService().getActivePart(); + if (part instanceof IEditorPart) { + IEditorInput input = ((IEditorPart) part).getEditorInput(); + Object resource = Util.getAdapter(input, resourceClass); + if (resource != null) { + selectionToPass = new StructuredSelection(resource); + } + } + } + } + + wizard.init(workbenchWindow.getWorkbench(), selectionToPass); + + IDialogSettings workbenchSettings = TeamUiPlugin.getDefault().getDialogSettings(); + IDialogSettings wizardSettings = workbenchSettings.getSection("NewWizardAction"); //$NON-NLS-1$ + if (wizardSettings == null) { + wizardSettings = workbenchSettings.addNewSection("NewWizardAction"); //$NON-NLS-1$ + } + wizard.setDialogSettings(wizardSettings); + wizard.setForcePreviousAndNextButtons(true); + + Shell parent = workbenchWindow.getShell(); + ValidatableWizardDialog dialog = new ValidatableWizardDialog(parent, wizard); + dialog.create(); + dialog.getShell().setSize(Math.max(SIZING_WIZARD_WIDTH, dialog.getShell().getSize().x), SIZING_WIZARD_HEIGHT); + //PlatformUI.getWorkbench().getHelpSystem().setHelp(dialog.getShell(), IWorkbenchHelpContextIds.NEW_WIZARD); + return dialog.open(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryWizardPage.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryWizardPage.java new file mode 100644 index 0000000..f2845ae --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/RepositoryWizardPage.java @@ -0,0 +1,117 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.commons.ui.team; + +import java.lang.reflect.InvocationTargetException; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.DialogPage; +import org.eclipse.jface.operation.IRunnableWithProgress; +import org.eclipse.jface.wizard.WizardPage; +import org.eclipse.mylyn.commons.repositories.RepositoryLocation; +import org.eclipse.mylyn.internal.provisional.commons.ui.dialogs.IValidatable; +import org.eclipse.swt.widgets.Composite; + +/** + * @author Steffen Pingel + */ +public class RepositoryWizardPage extends WizardPage implements IPartContainer, IAdaptable, IValidatable { + + private IAdaptable element; + + private RepositoryLocationPart part; + + private RepositoryLocation workingCopy; + + public RepositoryWizardPage(String pageName) { + super(pageName); + setPageComplete(false); + } + + public boolean canValidate() { + return part.canValidate(); + } + + public void createControl(Composite parent) { + initializeDialogUnits(parent); + + String message = getMessage(); + + part = doCreateRepositoryPart(); + part.setServiceLocator(this); + setControl(part.createContents(parent)); + Dialog.applyDialogFont(parent); + + setMessage(message); + } + + protected RepositoryLocationPart doCreateRepositoryPart() { + return new RepositoryLocationPart(getWorkingCopy()); + } + + public Object getAdapter(Class adapter) { + if (adapter == WizardPage.class) { + return this; + } + if (adapter == DialogPage.class) { + return this; + } + if (adapter == IPartContainer.class) { + return this; + } + return null; + } + + public IAdaptable getElement() { + return element; + } + + public RepositoryLocationPart getPart() { + return part; + } + + protected RepositoryLocation getWorkingCopy() { + if (workingCopy == null) { + workingCopy = (RepositoryLocation) getElement().getAdapter(RepositoryLocation.class); + } + return workingCopy; + } + + public boolean needsValidation() { + return part.needsValidation(); + } + + public void run(boolean fork, boolean cancelable, IRunnableWithProgress runnable) throws InvocationTargetException, + InterruptedException { + getContainer().run(fork, cancelable, runnable); + } + + /** + * Sets the element that owns properties shown on this page. + * + * @param element + * the element + */ + public void setElement(IAdaptable element) { + this.element = element; + } + + public void updateButtons() { + getContainer().updateButtons(); + } + + public void validate() { + part.validate(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/messages.properties b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/messages.properties new file mode 100644 index 0000000..f5362b5 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/commons/ui/team/messages.properties @@ -0,0 +1,19 @@ +RepositoryLocationPart_Anonymous=Anonymous +RepositoryLocationPart_Change_Settings=<a>Change Settings</a> +RepositoryLocationPart_Disconnected=Disconnected +RepositoryLocationPart_Enable_HTTP_Authentication=Enable HTTP Authentication +RepositoryLocationPart_Enable_Proxy_Authentication=Enable Proxy Authentication +RepositoryLocationPart_Enter_a_valid_server_url=Enter a valid server url. +RepositoryLocationPart_HTTP_Authentication=HTTP Authentication +RepositoryLocationPart_Label=&Label: +RepositoryLocationPart_Password=&Password: +RepositoryLocationPart_Proxy_Host=Proxy &Host: +RepositoryLocationPart_Proxy_Port=Proxy &Port: +RepositoryLocationPart_Proxy_Server_Configuration=Proxy Server Configuration +RepositoryLocationPart_Repository_is_valid=Repository is valid. +RepositoryLocationPart_Save_Password=Save Password +RepositoryLocationPart_Server=&Server: +RepositoryLocationPart_Unexpected_error_during_repository_validation=Unexpected error during repository validation. +RepositoryLocationPart_Use_global_Network_Connections_preferences=Use global Network Connections preferences +RepositoryLocationPart_User=&User: +RepositoryLocationPart_Validating_repository=Validating repository diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/EmptyRepositoryCategoriesFilter.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/EmptyRepositoryCategoriesFilter.java new file mode 100644 index 0000000..4000856 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/EmptyRepositoryCategoriesFilter.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.mylyn.commons.repositories.RepositoryCategory; + +/** + * @author Robert Elves + * @author Steffen Pingel + */ +public class EmptyRepositoryCategoriesFilter extends ViewerFilter { + + public EmptyRepositoryCategoriesFilter() { + } + + @Override + public boolean select(Viewer viewer, Object parentElement, Object element) { + if (element instanceof RepositoryCategory) { + return ((ITreeContentProvider) ((StructuredViewer) viewer).getContentProvider()).getChildren(element).length > 0; + } + return true; + } +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/Messages.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/Messages.java new file mode 100644 index 0000000..4a5c385 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/Messages.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2011 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import org.eclipse.osgi.util.NLS; + +public class Messages extends NLS { + private static final String BUNDLE_NAME = "org.eclipse.mylyn.internal.commons.ui.team.messages"; //$NON-NLS-1$ + + public static String NewRepositoryHandler_New_Repository; + + public static String RepositoriesView_Root; + static { + // initialize resource bundle + NLS.initializeMessages(BUNDLE_NAME, Messages.class); + } + + private Messages() { + } +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/NewRepositoryHandler.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/NewRepositoryHandler.java new file mode 100644 index 0000000..507eca3 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/NewRepositoryHandler.java @@ -0,0 +1,32 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import org.eclipse.core.commands.AbstractHandler; +import org.eclipse.core.commands.ExecutionEvent; +import org.eclipse.core.commands.ExecutionException; +import org.eclipse.core.commands.IHandler; +import org.eclipse.mylyn.commons.ui.team.RepositoryUi; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.handlers.HandlerUtil; + +/** + * @author Steffen Pingel + */ +public class NewRepositoryHandler extends AbstractHandler implements IHandler { + + public Object execute(ExecutionEvent event) throws ExecutionException { + IWorkbenchWindow workbenchWindow = HandlerUtil.getActiveWorkbenchWindowChecked(event); + return RepositoryUi.openNewRepositoryDialog(workbenchWindow, null); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoriesView.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoriesView.java new file mode 100644 index 0000000..94152ad --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoriesView.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import org.eclipse.mylyn.commons.repositories.RepositoryCategory; +import org.eclipse.mylyn.internal.provisional.commons.ui.GradientDrawer; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.ui.navigator.CommonNavigator; +import org.eclipse.ui.navigator.CommonViewer; +import org.eclipse.ui.part.IShowInTargetList; +import org.eclipse.ui.themes.IThemeManager; + +/** + * @author Steffen Pingel + */ +public class RepositoriesView extends CommonNavigator { + + private final RepositoryCategory rootCategory; + + public RepositoriesView() { + rootCategory = new RepositoryCategory(RepositoryCategory.ID_CATEGORY_ROOT, Messages.RepositoriesView_Root, 0); + } + + @Override + protected Object getInitialInput() { + return rootCategory; + } + + @Override + public void createPartControl(Composite aParent) { + super.createPartControl(aParent); + getCommonViewer().expandAll(); + } + + @Override + protected CommonViewer createCommonViewer(Composite aParent) { + CommonViewer viewer = super.createCommonViewer(aParent); + IThemeManager themeManager = getSite().getWorkbenchWindow().getWorkbench().getThemeManager(); + new GradientDrawer(themeManager, viewer) { + @Override + protected boolean shouldApplyGradient(org.eclipse.swt.widgets.Event event) { + return event.item.getData() instanceof RepositoryCategory; + } + }; + return viewer; + } + + @Override + public Object getAdapter(@SuppressWarnings("rawtypes") + Class adapter) { + // FIXME read targets from extension point? + if (adapter == IShowInTargetList.class) { + return new IShowInTargetList() { + public String[] getShowInTargetIds() { + return new String[] { "org.eclipse.mylyn.builds.navigator.builds" }; //$NON-NLS-1$ + } + + }; + } + return super.getAdapter(adapter); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryCategoryContentProvider.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryCategoryContentProvider.java new file mode 100644 index 0000000..4200a62 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryCategoryContentProvider.java @@ -0,0 +1,67 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.viewers.ITreeContentProvider; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.mylyn.commons.repositories.RepositoryCategory; + +public class RepositoryCategoryContentProvider implements ITreeContentProvider { + + private static final Map<String, RepositoryCategory> repositoryCategories = new HashMap<String, RepositoryCategory>(); + + public RepositoryCategoryContentProvider() { + RepositoryCategory catTasks = new RepositoryCategory(RepositoryCategory.ID_CATEGORY_TASKS, "Tasks", 0); //$NON-NLS-1$ + repositoryCategories.put(catTasks.getId(), catTasks); + RepositoryCategory catBugs = new RepositoryCategory(RepositoryCategory.ID_CATEGORY_BUGS, "Bugs", 100); //$NON-NLS-1$ + repositoryCategories.put(catBugs.getId(), catBugs); + RepositoryCategory catBuild = new RepositoryCategory(RepositoryCategory.ID_CATEGORY_BUILDS, "Builds", 200); //$NON-NLS-1$ + repositoryCategories.put(catBuild.getId(), catBuild); + RepositoryCategory catReview = new RepositoryCategory(RepositoryCategory.ID_CATEGORY_REVIEWS, "Reviews", 300); //$NON-NLS-1$ + repositoryCategories.put(catReview.getId(), catReview); + RepositoryCategory catOther = new RepositoryCategory(RepositoryCategory.ID_CATEGORY_OTHER, "Other", 400); //$NON-NLS-1$ + repositoryCategories.put(catOther.getId(), catOther); + } + + public void dispose() { + // ignore + + } + + public void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + // ignore + + } + + public Object[] getElements(Object inputElement) { + return repositoryCategories.values().toArray(); + } + + public Object[] getChildren(Object parentElement) { + // ignore + return null; + } + + public Object getParent(Object element) { + // ignore + return null; + } + + public boolean hasChildren(Object element) { + // ignore + return false; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryCategorySorter.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryCategorySorter.java new file mode 100644 index 0000000..fb69a24 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryCategorySorter.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import java.text.Collator; + +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerSorter; +import org.eclipse.mylyn.commons.repositories.RepositoryCategory; + +public class RepositoryCategorySorter extends ViewerSorter { + + public RepositoryCategorySorter() { + } + + public RepositoryCategorySorter(Collator collator) { + super(collator); + } + + @Override + public int compare(Viewer viewer, Object e1, Object e2) { + if (e1 instanceof RepositoryCategory && e2 instanceof RepositoryCategory) { + RepositoryCategory category1 = (RepositoryCategory) e1; + RepositoryCategory category2 = (RepositoryCategory) e2; + int result = category1.getRank() - category2.getRank(); + if (result != 0) { + return result; + } + } + // fall back to comparing by label + return super.compare(viewer, e1, e2); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryLabelProvider.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryLabelProvider.java new file mode 100644 index 0000000..e384c66 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryLabelProvider.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import org.eclipse.jface.viewers.LabelProvider; +import org.eclipse.mylyn.commons.repositories.RepositoryCategory; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.ISharedImages; +import org.eclipse.ui.internal.WorkbenchImages; + +/** + * @author Steffen Pingel + */ +public class RepositoryLabelProvider extends LabelProvider { + + @Override + public Image getImage(Object object) { + if (object instanceof RepositoryCategory) { + return WorkbenchImages.getImage(ISharedImages.IMG_OBJ_FOLDER); + } + return null; + } + + @Override + public String getText(Object object) { + if (object instanceof RepositoryCategory) { + return ((RepositoryCategory) object).getLabel(); + } + return null; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryLocationValueProperty.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryLocationValueProperty.java new file mode 100644 index 0000000..1c36dd8 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/RepositoryLocationValueProperty.java @@ -0,0 +1,104 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import java.beans.PropertyChangeEvent; +import java.beans.PropertyChangeListener; + +import org.eclipse.core.databinding.observable.Diffs; +import org.eclipse.core.databinding.observable.IDiff; +import org.eclipse.core.databinding.property.INativePropertyListener; +import org.eclipse.core.databinding.property.IProperty; +import org.eclipse.core.databinding.property.ISimplePropertyListener; +import org.eclipse.core.databinding.property.NativePropertyListener; +import org.eclipse.core.databinding.property.value.SimpleValueProperty; +import org.eclipse.mylyn.commons.repositories.RepositoryLocation; + +/** + * @author Steffen Pingel + */ +public class RepositoryLocationValueProperty extends SimpleValueProperty { + + private class PrivatePropertyChangeListener extends NativePropertyListener implements PropertyChangeListener { + + public PrivatePropertyChangeListener(IProperty property, ISimplePropertyListener listener) { + super(property, listener); + } + + @Override + protected void doAddTo(Object source) { + ((RepositoryLocation) source).addChangeListener(this); + } + + @Override + protected void doRemoveFrom(Object source) { + ((RepositoryLocation) source).removeChangeListener(this); + } + + public void propertyChange(PropertyChangeEvent evt) { + if (evt.getPropertyName() == null || key.equals(evt.getPropertyName())) { + Object oldValue = evt.getOldValue(); + Object newValue = evt.getNewValue(); + IDiff diff; + if (evt.getPropertyName() == null || oldValue == null || newValue == null) { + diff = null; + } else { + diff = Diffs.createValueDiff(oldValue, newValue); + } + fireChange(evt.getSource(), diff); + } + } + + } + + private final String key; + + private final String defaultValue; + + public RepositoryLocationValueProperty(String key, String defaultValue) { + this.key = key; + this.defaultValue = defaultValue; + } + + public Object getValueType() { + return String.class; + } + + @Override + protected Object doGetValue(Object source) { +// if ("uri".equals(key)) { +// URI uri = ((RepositoryLocation) source).getUri(); +// return (uri != null) ? uri.toString() : uri; +// } + String value = ((RepositoryLocation) source).getProperty(key); + return (value != null) ? value : defaultValue; + } + + @Override + protected void doSetValue(Object source, Object value) { +// if ("uri".equals(key)) { +// try { +// ((RepositoryLocation) source).setUri(new URI((String) value)); +// } catch (URISyntaxException e) { +// // ignore +// } +// } else { + ((RepositoryLocation) source).setProperty(key, (value != null) ? value.toString() : null); +// } + } + + @Override + public INativePropertyListener adaptListener(final ISimplePropertyListener listener) { + return new PrivatePropertyChangeListener(this, listener); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/ShowInMenuContribution.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/ShowInMenuContribution.java new file mode 100644 index 0000000..a4e42ed --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/ShowInMenuContribution.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team; + +import org.eclipse.jface.action.ContributionItem; +import org.eclipse.jface.action.IContributionItem; +import org.eclipse.swt.widgets.Menu; +import org.eclipse.ui.IWorkbenchWindow; +import org.eclipse.ui.PlatformUI; +import org.eclipse.ui.actions.ContributionItemFactory; + +/** + * @author Steffen Pingel + */ +public class ShowInMenuContribution extends ContributionItem { + + private IContributionItem item; + + public ShowInMenuContribution() { + } + + public ShowInMenuContribution(String id) { + super(id); + } + + @Override + public void fill(Menu menu, int index) { + IWorkbenchWindow window = PlatformUI.getWorkbench().getActiveWorkbenchWindow(); + if (window != null) { + if (item != null) { + item.dispose(); + } + item = ContributionItemFactory.VIEWS_SHOW_IN.create(window); + item.fill(menu, index); + } + } + + @Override + public void dispose() { + if (item != null) { + item.dispose(); + item = null; + } + super.dispose(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/TeamUiPlugin.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/TeamUiPlugin.java new file mode 100644 index 0000000..007d865 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/TeamUiPlugin.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2010 Tasktop Technologies and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Tasktop Technologies - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylyn.internal.commons.ui.team; + +import org.eclipse.ui.plugin.AbstractUIPlugin; +import org.osgi.framework.BundleContext; + +/** + * @author Steffen Pingel + */ +public class TeamUiPlugin extends AbstractUIPlugin { + + public static final String ID_PLUGIN = "org.eclipse.mylyn.commons.team"; //$NON-NLS-1$ + + private static TeamUiPlugin plugin; + + /** + * The constructor + */ + public TeamUiPlugin() { + } + + @Override + public void start(BundleContext context) throws Exception { + super.start(context); + plugin = this; + } + + @Override + public void stop(BundleContext context) throws Exception { + plugin = null; + super.stop(context); + } + + /** + * Returns the shared instance + * + * @return the shared instance + */ + public static TeamUiPlugin getDefault() { + return plugin; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/messages.properties b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/messages.properties new file mode 100644 index 0000000..4395872 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/messages.properties @@ -0,0 +1,2 @@ +NewRepositoryHandler_New_Repository=New Repository +RepositoriesView_Root=Root diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizard.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizard.java new file mode 100644 index 0000000..557b65b --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizard.java @@ -0,0 +1,172 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylyn.internal.commons.ui.team.wizards; + +import java.util.StringTokenizer; + +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.IWizard; +import org.eclipse.jface.wizard.Wizard; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.internal.IWorkbenchGraphicConstants; +import org.eclipse.ui.internal.WorkbenchImages; +import org.eclipse.ui.internal.WorkbenchMessages; +import org.eclipse.ui.wizards.IWizardCategory; +import org.eclipse.ui.wizards.IWizardDescriptor; + +/** + * The new wizard is responsible for allowing the user to choose which new (nested) wizard to run. The set of available + * new wizards comes from the new extension point. + */ +public class NewRepositoryWizard extends Wizard { + + private static final String CATEGORY_SEPARATOR = "/"; //$NON-NLS-1$ + + private String categoryId = null; + + private NewRepositoryWizardSelectionPage mainPage; + + private boolean projectsOnly = false; + + private IStructuredSelection selection; + + private IWorkbench workbench; + + /** + * Create the wizard pages + */ + @Override + public void addPages() { + IWizardCategory root = NewRepositoryWizardRegistry.getInstance().getRootCategory(); + IWizardDescriptor[] primary = NewRepositoryWizardRegistry.getInstance().getPrimaryWizards(); + + if (categoryId != null) { + IWizardCategory categories = root; + StringTokenizer familyTokenizer = new StringTokenizer(categoryId, CATEGORY_SEPARATOR); + while (familyTokenizer.hasMoreElements()) { + categories = getChildWithID(categories, familyTokenizer.nextToken()); + if (categories == null) { + break; + } + } + if (categories != null) { + root = categories; + } + } + + mainPage = new NewRepositoryWizardSelectionPage(workbench, selection, root, primary, projectsOnly); + addPage(mainPage); + } + + /** + * Returns the id of the category of wizards to show or <code>null</code> to show all categories. If no entries can + * be found with this id then all categories are shown. + * + * @return String or <code>null</code>. + */ + public String getCategoryId() { + return categoryId; + } + + /** + * Returns the child collection element for the given id + */ + private IWizardCategory getChildWithID(IWizardCategory parent, String id) { + IWizardCategory[] children = parent.getCategories(); + for (int i = 0; i < children.length; ++i) { + IWizardCategory currentChild = children[i]; + if (currentChild.getId().equals(id)) { + return currentChild; + } + } + return null; + } + + /** + * Lazily create the wizards pages + * + * @param aWorkbench + * the workbench + * @param currentSelection + * the current selection + */ + public void init(IWorkbench aWorkbench, IStructuredSelection currentSelection) { + this.workbench = aWorkbench; + this.selection = currentSelection; + + if (getWindowTitle() == null) { + // No title supplied. Set the default title + if (projectsOnly) { + setWindowTitle(WorkbenchMessages.NewProject_title); + } else { + setWindowTitle(WorkbenchMessages.NewWizard_title); + } + } + setDefaultPageImageDescriptor(WorkbenchImages.getImageDescriptor(IWorkbenchGraphicConstants.IMG_WIZBAN_NEW_WIZ)); + setNeedsProgressMonitor(true); + } + + /** + * The user has pressed Finish. Instruct self's pages to finish, and answer a boolean indicating success. + * + * @return boolean + */ + @Override + public boolean performFinish() { + //save our selection state + mainPage.saveWidgetValues(); + // if we're finishing from the main page then perform finish on the selected wizard. + if (getContainer().getCurrentPage() == mainPage) { + if (mainPage.canFinishEarly()) { + IWizard wizard = mainPage.getSelectedNode().getWizard(); + wizard.setContainer(getContainer()); + return wizard.performFinish(); + } + } + return true; + } + + /** + * Sets the id of the category of wizards to show or <code>null</code> to show all categories. If no entries can be + * found with this id then all categories are shown. + * + * @param id + * may be <code>null</code>. + */ + public void setCategoryId(String id) { + categoryId = id; + } + + /** + * Sets the projects only flag. If <code>true</code> only projects will be shown in this wizard. + * + * @param b + * if only projects should be shown + */ + public void setProjectsOnly(boolean b) { + projectsOnly = b; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.wizard.IWizard#canFinish() + */ + @Override + public boolean canFinish() { + // we can finish if the first page is current and the the page can finish early. + if (getContainer().getCurrentPage() == mainPage) { + if (mainPage.canFinishEarly()) { + return true; + } + } + return super.canFinish(); + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardCollectionComparator.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardCollectionComparator.java new file mode 100644 index 0000000..82bbfb0 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardCollectionComparator.java @@ -0,0 +1,71 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team.wizards; + +import org.eclipse.jface.viewers.IBasicPropertyConstants; +import org.eclipse.jface.viewers.ViewerComparator; +import org.eclipse.ui.internal.dialogs.WizardCollectionElement; +import org.eclipse.ui.internal.dialogs.WorkbenchWizardElement; +import org.eclipse.ui.internal.registry.WizardsRegistryReader; + +/** + * A Viewer element sorter that sorts Elements by their name attribute. Note that capitalization differences are not + * considered by this sorter, so a < B < c. NOTE one exception to the above: an element with the system's reserved name + * for base Wizards will always be sorted such that it will ultimately be placed at the beginning of the sorted result. + */ +class NewWizardCollectionComparator extends ViewerComparator { + /** + * Static instance of this class. + */ + public final static NewWizardCollectionComparator INSTANCE = new NewWizardCollectionComparator(); + + /** + * Creates an instance of <code>NewWizardCollectionSorter</code>. Since this is a stateless sorter, it is only + * accessible as a singleton; the private visibility of this constructor ensures this. + */ + private NewWizardCollectionComparator() { + super(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.ViewerSorter#category(java.lang.Object) + */ + @Override + public int category(Object element) { + if (element instanceof WorkbenchWizardElement) { + return -1; + } + if (element instanceof WizardCollectionElement) { + String id = ((WizardCollectionElement) element).getId(); + if (WizardsRegistryReader.GENERAL_WIZARD_CATEGORY.equals(id)) { + return 1; + } + if (WizardsRegistryReader.UNCATEGORIZED_WIZARD_CATEGORY.equals(id)) { + return 3; + } + if (WizardsRegistryReader.FULL_EXAMPLES_WIZARD_CATEGORY.equals(id)) { + return 4; + } + return 2; + } + return super.category(element); + } + + /** + * Return true if this sorter is affected by a property change of propertyName on the specified element. + */ + @Override + public boolean isSorterProperty(Object object, String propertyId) { + return propertyId.equals(IBasicPropertyConstants.P_TEXT); + } +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardNewPage.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardNewPage.java new file mode 100644 index 0000000..e93ff36 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardNewPage.java @@ -0,0 +1,727 @@ +/******************************************************************************* + * Copyright (c) 2000, 2010 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylyn.internal.commons.ui.team.wizards; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.jface.dialogs.Dialog; +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.DoubleClickEvent; +import org.eclipse.jface.viewers.IDoubleClickListener; +import org.eclipse.jface.viewers.ISelectionChangedListener; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.SelectionChangedEvent; +import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.jface.wizard.IWizardContainer; +import org.eclipse.jface.wizard.IWizardContainer2; +import org.eclipse.swt.SWT; +import org.eclipse.swt.custom.CLabel; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.DisposeListener; +import org.eclipse.swt.events.SelectionAdapter; +import org.eclipse.swt.events.SelectionEvent; +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.Control; +import org.eclipse.swt.widgets.Label; +import org.eclipse.ui.IWorkbenchWizard; +import org.eclipse.ui.activities.WorkbenchActivityHelper; +import org.eclipse.ui.dialogs.FilteredTree; +import org.eclipse.ui.internal.WorkbenchMessages; +import org.eclipse.ui.internal.dialogs.DialogUtil; +import org.eclipse.ui.internal.dialogs.WizardActivityFilter; +import org.eclipse.ui.internal.dialogs.WizardContentProvider; +import org.eclipse.ui.internal.dialogs.WizardPatternFilter; +import org.eclipse.ui.internal.dialogs.WizardTagFilter; +import org.eclipse.ui.internal.dialogs.WorkbenchWizardElement; +import org.eclipse.ui.internal.dialogs.WorkbenchWizardNode; +import org.eclipse.ui.model.AdaptableList; +import org.eclipse.ui.model.WorkbenchLabelProvider; +import org.eclipse.ui.wizards.IWizardCategory; +import org.eclipse.ui.wizards.IWizardDescriptor; + +/** + * New wizard selection tab that allows the user to select a registered 'New' wizard to be launched. + */ +class NewRepositoryWizardNewPage implements ISelectionChangedListener { + + // id constants + private static final String DIALOG_SETTING_SECTION_NAME = "NewWizardSelectionPage."; //$NON-NLS-1$ + + private final static int SIZING_LISTS_HEIGHT = 200; + + private final static int SIZING_VIEWER_WIDTH = 300; + + private final static String STORE_EXPANDED_CATEGORIES_ID = DIALOG_SETTING_SECTION_NAME + + "STORE_EXPANDED_CATEGORIES_ID"; //$NON-NLS-1$ + + private final static String STORE_SELECTED_ID = DIALOG_SETTING_SECTION_NAME + "STORE_SELECTED_ID"; //$NON-NLS-1$ + + private final NewRepositoryWizardSelectionPage page; + + private FilteredTree filteredTree; + + private WizardPatternFilter filteredTreeFilter; + + //Keep track of the wizards we have previously selected + private final Hashtable selectedWizards = new Hashtable(); + + private IDialogSettings settings; + + private Button showAllCheck; + + private IWizardCategory wizardCategories; + + private IWizardDescriptor[] primaryWizards; + + private CLabel descImageCanvas; + + private final Map imageTable = new HashMap(); + + private IWizardDescriptor selectedElement; + + private final WizardActivityFilter filter = new WizardActivityFilter(); + + private boolean needShowAll; + + private final boolean projectsOnly; + + private final ViewerFilter projectFilter = new WizardTagFilter(new String[] { WorkbenchWizardElement.TAG_PROJECT }); + + /** + * Create an instance of this class + * + * @param mainPage + * @param wizardCategories + * @param primaryWizards + * @param projectsOnly + */ + public NewRepositoryWizardNewPage(NewRepositoryWizardSelectionPage mainPage, IWizardCategory wizardCategories, + IWizardDescriptor[] primaryWizards, boolean projectsOnly) { + this.page = mainPage; + this.wizardCategories = wizardCategories; + this.primaryWizards = primaryWizards; + this.projectsOnly = projectsOnly; + + trimPrimaryWizards(); + + if (this.primaryWizards.length > 0) { + if (allPrimary(wizardCategories)) { + this.wizardCategories = null; // dont bother considering the categories as all wizards are primary + needShowAll = false; + } else { + needShowAll = !allActivityEnabled(wizardCategories); + } + } else { + needShowAll = !allActivityEnabled(wizardCategories); + } + } + + /** + * @param category + * the wizard category + * @return whether all of the wizards in the category are enabled via activity filtering + */ + private boolean allActivityEnabled(IWizardCategory category) { + IWizardDescriptor[] wizards = category.getWizards(); + for (IWizardDescriptor wizard : wizards) { + if (WorkbenchActivityHelper.filterItem(wizard)) { + return false; + } + } + + IWizardCategory[] children = category.getCategories(); + for (int i = 0; i < children.length; i++) { + if (!allActivityEnabled(children[i])) { + return false; + } + } + + return true; + } + + /** + * Remove all primary wizards that are not in the wizard collection + */ + private void trimPrimaryWizards() { + ArrayList newPrimaryWizards = new ArrayList(primaryWizards.length); + + if (wizardCategories == null) { + return;//No categories so nothing to trim + } + + for (IWizardDescriptor primaryWizard : primaryWizards) { + if (wizardCategories.findWizard(primaryWizard.getId()) != null) { + newPrimaryWizards.add(primaryWizard); + } + } + + primaryWizards = (WorkbenchWizardElement[]) newPrimaryWizards.toArray(new WorkbenchWizardElement[newPrimaryWizards.size()]); + } + + /** + * @param category + * the wizard category + * @return whether all wizards in the category are considered primary + */ + private boolean allPrimary(IWizardCategory category) { + IWizardDescriptor[] wizards = category.getWizards(); + for (IWizardDescriptor wizard2 : wizards) { + IWizardDescriptor wizard = wizard2; + if (!isPrimary(wizard)) { + return false; + } + } + + IWizardCategory[] children = category.getCategories(); + for (int i = 0; i < children.length; i++) { + if (!allPrimary(children[i])) { + return false; + } + } + + return true; + } + + /** + * @param wizard + * @return whether the given wizard is primary + */ + private boolean isPrimary(IWizardDescriptor wizard) { + for (IWizardDescriptor primaryWizard : primaryWizards) { + if (primaryWizard.equals(wizard)) { + return true; + } + } + + return false; + } + + /** + * @since 3.0 + */ + public void activate() { + page.setDescription(WorkbenchMessages.NewWizardNewPage_description); + } + + /** + * Create this tab's visual components + * + * @param parent + * Composite + * @return Control + */ + protected Control createControl(Composite parent) { + + Font wizardFont = parent.getFont(); + // top level group + Composite outerContainer = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + outerContainer.setLayout(layout); + + Label wizardLabel = new Label(outerContainer, SWT.NONE); + GridData data = new GridData(SWT.BEGINNING, SWT.FILL, false, true); + outerContainer.setLayoutData(data); + wizardLabel.setFont(wizardFont); + wizardLabel.setText(WorkbenchMessages.NewWizardNewPage_wizardsLabel); + + Composite innerContainer = new Composite(outerContainer, SWT.NONE); + layout = new GridLayout(2, false); + layout.marginHeight = 0; + layout.marginWidth = 0; + innerContainer.setLayout(layout); + innerContainer.setFont(wizardFont); + data = new GridData(SWT.FILL, SWT.FILL, true, true); + innerContainer.setLayoutData(data); + + filteredTree = createFilteredTree(innerContainer); + createOptionsButtons(innerContainer); + + createImage(innerContainer); + + updateDescription(null); + + // wizard actions pane...create SWT table directly to + // get single selection mode instead of multi selection. + restoreWidgetValues(); + + return outerContainer; + } + + /** + * Create a new FilteredTree in the parent. + * + * @param parent + * the parent <code>Composite</code>. + * @since 3.0 + */ + protected FilteredTree createFilteredTree(Composite parent) { + Composite composite = new Composite(parent, SWT.NONE); + GridLayout layout = new GridLayout(); + layout.marginHeight = 0; + layout.marginWidth = 0; + composite.setLayout(layout); + + GridData data = new GridData(SWT.FILL, SWT.FILL, true, true); + data.widthHint = SIZING_VIEWER_WIDTH; + data.horizontalSpan = 2; + data.grabExcessHorizontalSpace = true; + data.grabExcessVerticalSpace = true; + + boolean needsHint = DialogUtil.inRegularFontMode(parent); + + //Only give a height hint if the dialog is going to be too small + if (needsHint) { + data.heightHint = SIZING_LISTS_HEIGHT; + } + composite.setLayoutData(data); + + filteredTreeFilter = new WizardPatternFilter(); + FilteredTree filterTree = new FilteredTree(composite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER, + filteredTreeFilter, true); + + final TreeViewer treeViewer = filterTree.getViewer(); + treeViewer.setContentProvider(new WizardContentProvider()); + treeViewer.setLabelProvider(new WorkbenchLabelProvider()); + treeViewer.setComparator(NewWizardCollectionComparator.INSTANCE); + treeViewer.addSelectionChangedListener(this); + + ArrayList inputArray = new ArrayList(); + + for (IWizardDescriptor primaryWizard : primaryWizards) { + inputArray.add(primaryWizard); + } + + boolean expandTop = false; + + if (wizardCategories != null) { + if (wizardCategories.getParent() == null) { + IWizardCategory[] children = wizardCategories.getCategories(); + for (IWizardCategory element : children) { + inputArray.add(element); + } + } else { + expandTop = true; + inputArray.add(wizardCategories); + } + } + + // ensure the category is expanded. If there is a remembered expansion it will be set later. + if (expandTop) { + treeViewer.setAutoExpandLevel(2); + } + + AdaptableList input = new AdaptableList(inputArray); + + treeViewer.setInput(input); + + filterTree.setBackground(parent.getDisplay().getSystemColor(SWT.COLOR_WIDGET_BACKGROUND)); + + treeViewer.getTree().setFont(parent.getFont()); + + treeViewer.addDoubleClickListener(new IDoubleClickListener() { + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IDoubleClickListener#doubleClick(org.eclipse.jface.viewers.DoubleClickEvent) + */ + public void doubleClick(DoubleClickEvent event) { + IStructuredSelection s = (IStructuredSelection) event.getSelection(); + selectionChanged(new SelectionChangedEvent(event.getViewer(), s)); + + Object element = s.getFirstElement(); + if (treeViewer.isExpandable(element)) { + treeViewer.setExpandedState(element, !treeViewer.getExpandedState(element)); + } else if (element instanceof WorkbenchWizardElement) { + page.advanceToNextPageOrFinish(); + } + } + }); + + treeViewer.addFilter(filter); + + if (projectsOnly) { + treeViewer.addFilter(projectFilter); + } + + Dialog.applyDialogFont(filterTree); + return filterTree; + } + + /** + * Create the Show All and help buttons at the bottom of the page. + * + * @param parent + * the parent composite on which to create the widgets + */ + private void createOptionsButtons(Composite parent) { + if (needShowAll) { + showAllCheck = new Button(parent, SWT.CHECK); + GridData data = new GridData(); + showAllCheck.setLayoutData(data); + showAllCheck.setFont(parent.getFont()); + showAllCheck.setText(WorkbenchMessages.NewWizardNewPage_showAll); + showAllCheck.setSelection(false); + + // flipping tabs updates the selected node + showAllCheck.addSelectionListener(new SelectionAdapter() { + + // the delta of expanded elements between the last 'show all' + // and the current 'no show all' + private Object[] delta = new Object[0]; + + @Override + public void widgetSelected(SelectionEvent e) { + boolean showAll = showAllCheck.getSelection(); + + if (showAll) { + filteredTree.getViewer().getControl().setRedraw(false); + } else { + // get the inital expanded elements when going from show + // all-> no show all. + // this isnt really the delta yet, we're just reusing + // the variable. + delta = filteredTree.getViewer().getExpandedElements(); + } + + try { + if (showAll) { + filteredTree.getViewer().resetFilters(); + filteredTree.getViewer().addFilter(filteredTreeFilter); + if (projectsOnly) { + filteredTree.getViewer().addFilter(projectFilter); + } + + // restore the expanded elements that were present + // in the last show all state but not in the 'no + // show all' state. + Object[] currentExpanded = filteredTree.getViewer().getExpandedElements(); + Object[] expanded = new Object[delta.length + currentExpanded.length]; + System.arraycopy(currentExpanded, 0, expanded, 0, currentExpanded.length); + System.arraycopy(delta, 0, expanded, currentExpanded.length, delta.length); + filteredTree.getViewer().setExpandedElements(expanded); + } else { + filteredTree.getViewer().addFilter(filter); + if (projectsOnly) { + filteredTree.getViewer().addFilter(projectFilter); + } + } + filteredTree.getViewer().refresh(false); + + if (!showAll) { + // if we're going from show all -> no show all + // record the elements that were expanded in the + // 'show all' state but not the 'no show all' state + // (because they didnt exist). + Object[] newExpanded = filteredTree.getViewer().getExpandedElements(); + List deltaList = new ArrayList(Arrays.asList(delta)); + deltaList.removeAll(Arrays.asList(newExpanded)); + } + } finally { + if (showAll) { + filteredTree.getViewer().getControl().setRedraw(true); + } + } + } + }); + } + } + + /** + * Create the image controls. + * + * @param parent + * the parent <code>Composite</code>. + * @since 3.0 + */ + private void createImage(Composite parent) { + descImageCanvas = new CLabel(parent, SWT.NONE); + GridData data = new GridData(GridData.HORIZONTAL_ALIGN_BEGINNING | GridData.VERTICAL_ALIGN_BEGINNING); + data.widthHint = 0; + data.heightHint = 0; + descImageCanvas.setLayoutData(data); + + // hook a listener to get rid of cached images. + descImageCanvas.addDisposeListener(new DisposeListener() { + + /* (non-Javadoc) + * @see org.eclipse.swt.events.DisposeListener#widgetDisposed(org.eclipse.swt.events.DisposeEvent) + */ + public void widgetDisposed(DisposeEvent e) { + for (Iterator i = imageTable.values().iterator(); i.hasNext();) { + ((Image) i.next()).dispose(); + } + imageTable.clear(); + } + }); + } + + /** + * Expands the wizard categories in this page's category viewer that were expanded last time this page was used. If + * a category that was previously expanded no longer exists then it is ignored. + */ + protected void expandPreviouslyExpandedCategories() { + String[] expandedCategoryPaths = settings.getArray(STORE_EXPANDED_CATEGORIES_ID); + if (expandedCategoryPaths == null || expandedCategoryPaths.length == 0) { + return; + } + + List categoriesToExpand = new ArrayList(expandedCategoryPaths.length); + + if (wizardCategories != null) { + for (String expandedCategoryPath : expandedCategoryPaths) { + IWizardCategory category = wizardCategories.findCategory(new Path(expandedCategoryPath)); + if (category != null) { + categoriesToExpand.add(category); + } + } + } + + if (!categoriesToExpand.isEmpty()) { + filteredTree.getViewer().setExpandedElements(categoriesToExpand.toArray()); + } + + } + + /** + * Returns the single selected object contained in the passed selectionEvent, or <code>null</code> if the + * selectionEvent contains either 0 or 2+ selected objects. + */ + protected Object getSingleSelection(IStructuredSelection selection) { + return selection.size() == 1 ? selection.getFirstElement() : null; + } + + /** + * Set self's widgets to the values that they held last time this page was open + */ + protected void restoreWidgetValues() { + expandPreviouslyExpandedCategories(); + selectPreviouslySelected(); + } + + /** + * Store the current values of self's widgets so that they can be restored in the next instance of self + */ + public void saveWidgetValues() { + storeExpandedCategories(); + storeSelectedCategoryAndWizard(); + } + + /** + * The user selected either new wizard category(s) or wizard element(s). Proceed accordingly. + * + * @param selectionEvent + * ISelection + */ + public void selectionChanged(SelectionChangedEvent selectionEvent) { + page.setErrorMessage(null); + page.setMessage(null); + + Object selectedObject = getSingleSelection((IStructuredSelection) selectionEvent.getSelection()); + + if (selectedObject instanceof IWizardDescriptor) { + if (selectedObject == selectedElement) { + return; + } + updateWizardSelection((IWizardDescriptor) selectedObject); + } else { + selectedElement = null; + page.setHasPages(false); + page.setCanFinishEarly(false); + page.selectWizardNode(null); + updateDescription(null); + } + } + + /** + * Selects the wizard category and wizard in this page that were selected last time this page was used. If a + * category or wizard that was previously selected no longer exists then it is ignored. + */ + protected void selectPreviouslySelected() { + String selectedId = settings.get(STORE_SELECTED_ID); + if (selectedId == null) { + return; + } + + if (wizardCategories == null) { + return; + } + + Object selected = wizardCategories.findCategory(new Path(selectedId)); + + if (selected == null) { + selected = wizardCategories.findWizard(selectedId); + + if (selected == null) { + // if we cant find either a category or a wizard, abort. + return; + } + } + + //work around for 62039 + final StructuredSelection selection = new StructuredSelection(selected); + filteredTree.getViewer().getControl().getDisplay().asyncExec(new Runnable() { + public void run() { + filteredTree.getViewer().setSelection(selection, true); + } + }); + } + + /** + * Set the dialog store to use for widget value storage and retrieval + * + * @param settings + * IDialogSettings + */ + public void setDialogSettings(IDialogSettings settings) { + this.settings = settings; + } + + /** + * Stores the collection of currently-expanded categories in this page's dialog store, in order to recreate this + * page's state in the next instance of this page. + */ + protected void storeExpandedCategories() { + Object[] expandedElements = filteredTree.getViewer().getExpandedElements(); + List expandedElementPaths = new ArrayList(expandedElements.length); + for (int i = 0; i < expandedElements.length; ++i) { + if (expandedElements[i] instanceof IWizardCategory) { + expandedElementPaths.add(((IWizardCategory) expandedElements[i]).getPath().toString()); + } + } + settings.put(STORE_EXPANDED_CATEGORIES_ID, + (String[]) expandedElementPaths.toArray(new String[expandedElementPaths.size()])); + } + + /** + * Stores the currently-selected element in this page's dialog store, in order to recreate this page's state in the + * next instance of this page. + */ + protected void storeSelectedCategoryAndWizard() { + Object selected = getSingleSelection((IStructuredSelection) filteredTree.getViewer().getSelection()); + + if (selected != null) { + if (selected instanceof IWizardCategory) { + settings.put(STORE_SELECTED_ID, ((IWizardCategory) selected).getPath().toString()); + } else { + // else its a wizard + settings.put(STORE_SELECTED_ID, ((IWizardDescriptor) selected).getId()); + } + } + } + + /** + * Update the current description controls. + * + * @param selectedObject + * the new wizard + * @since 3.0 + */ + private void updateDescription(IWizardDescriptor selectedObject) { + String string = ""; //$NON-NLS-1$ + if (selectedObject != null) { + string = selectedObject.getDescription(); + } + + page.setDescription(string); + + if (hasImage(selectedObject)) { + ImageDescriptor descriptor = null; + if (selectedObject != null) { + descriptor = selectedObject.getDescriptionImage(); + } + + if (descriptor != null) { + GridData data = (GridData) descImageCanvas.getLayoutData(); + data.widthHint = SWT.DEFAULT; + data.heightHint = SWT.DEFAULT; + Image image = (Image) imageTable.get(descriptor); + if (image == null) { + image = descriptor.createImage(false); + imageTable.put(descriptor, image); + } + descImageCanvas.setImage(image); + } + } else { + GridData data = (GridData) descImageCanvas.getLayoutData(); + data.widthHint = 0; + data.heightHint = 0; + descImageCanvas.setImage(null); + } + + descImageCanvas.getParent().layout(true); + filteredTree.getViewer().getTree().showSelection(); + + IWizardContainer container = page.getWizard().getContainer(); + if (container instanceof IWizardContainer2) { + ((IWizardContainer2) container).updateSize(); + } + } + + /** + * Tests whether the given wizard has an associated image. + * + * @param selectedObject + * the wizard to test + * @return whether the given wizard has an associated image + */ + private boolean hasImage(IWizardDescriptor selectedObject) { + if (selectedObject == null) { + return false; + } + + if (selectedObject.getDescriptionImage() != null) { + return true; + } + + return false; + } + + /** + * @param selectedObject + */ + private void updateWizardSelection(IWizardDescriptor selectedObject) { + selectedElement = selectedObject; + WorkbenchWizardNode selectedNode; + if (selectedWizards.containsKey(selectedObject)) { + selectedNode = (WorkbenchWizardNode) selectedWizards.get(selectedObject); + } else { + selectedNode = new WorkbenchWizardNode(page, selectedObject) { + @Override + public IWorkbenchWizard createWizard() throws CoreException { + return wizardElement.createWizard(); + } + }; + selectedWizards.put(selectedObject, selectedNode); + } + + page.setCanFinishEarly(selectedObject.canFinishEarly()); + page.setHasPages(selectedObject.hasPages()); + page.selectWizardNode(selectedNode); + + updateDescription(selectedObject); + } +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardRegistry.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardRegistry.java new file mode 100644 index 0000000..2f8be74 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardRegistry.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2004, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Tasktop Technologies - improvements for Mylyn + *******************************************************************************/ + +package org.eclipse.mylyn.internal.commons.ui.team.wizards; + +import org.eclipse.mylyn.internal.commons.ui.team.TeamUiPlugin; +import org.eclipse.ui.internal.wizards.AbstractExtensionWizardRegistry; +import org.eclipse.ui.internal.wizards.NewWizardRegistry; + +/** + * Based on {@link NewWizardRegistry}. + * + * @author Steffen Pingel + */ +public final class NewRepositoryWizardRegistry extends AbstractExtensionWizardRegistry { + + private static NewRepositoryWizardRegistry singleton; + + /** + * Return the singleton instance of this class. + * + * @return the singleton instance of this class + */ + public static synchronized NewRepositoryWizardRegistry getInstance() { + if (singleton == null) { + singleton = new NewRepositoryWizardRegistry(); + } + return singleton; + } + + /** + * Private constructor. + */ + private NewRepositoryWizardRegistry() { + } + + @Override + protected String getExtensionPoint() { + return "newWizards"; //$NON-NLS-1$ + } + + @Override + protected String getPlugin() { + return TeamUiPlugin.ID_PLUGIN; + } + +} diff --git a/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardSelectionPage.java b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardSelectionPage.java new file mode 100644 index 0000000..9383354 --- a/dev/null +++ b/stubs/org.eclipse.mylyn.commons.team/src/org/eclipse/mylyn/internal/commons/ui/team/wizards/NewRepositoryWizardSelectionPage.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2000, 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.mylyn.internal.commons.ui.team.wizards; + +import org.eclipse.jface.dialogs.IDialogSettings; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.wizard.WizardDialog; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Control; +import org.eclipse.ui.IWorkbench; +import org.eclipse.ui.internal.IWorkbenchHelpContextIds; +import org.eclipse.ui.internal.WorkbenchMessages; +import org.eclipse.ui.internal.activities.ws.WorkbenchTriggerPoints; +import org.eclipse.ui.internal.dialogs.WorkbenchWizardSelectionPage; +import org.eclipse.ui.wizards.IWizardCategory; +import org.eclipse.ui.wizards.IWizardDescriptor; + +/** + * New wizard selection tab that allows the user to either select a registered 'New' wizard to be launched, or to select + * a solution or projects to be retrieved from an available server. This page contains two visual tabs that allow the + * user to perform these tasks. Temporarily has two inner pages. The new format page is used if the system is currently + * aware of activity categories. + */ +class NewRepositoryWizardSelectionPage extends WorkbenchWizardSelectionPage { + + private final IWizardCategory wizardCategories; + + // widgets + private NewRepositoryWizardNewPage newResourcePage; + + private final IWizardDescriptor[] primaryWizards; + + private final boolean projectsOnly; + + private boolean canFinishEarly = false, hasPages = true; + + /** + * Create an instance of this class. + * + * @param workbench + * the workbench + * @param selection + * the current selection + * @param root + * the wizard root element + * @param primary + * the primary wizard elements + * @param projectsOnly + * if only projects should be shown + */ + public NewRepositoryWizardSelectionPage(IWorkbench workbench, IStructuredSelection selection, IWizardCategory root, + IWizardDescriptor[] primary, boolean projectsOnly) { + super("newWizardSelectionPage", workbench, selection, null, WorkbenchTriggerPoints.NEW_WIZARDS);//$NON-NLS-1$ + + setTitle(WorkbenchMessages.NewWizardSelectionPage_description); + wizardCategories = root; + primaryWizards = primary; + this.projectsOnly = projectsOnly; + } + + /** + * Makes the next page visible. + */ + public void advanceToNextPageOrFinish() { + if (canFlipToNextPage()) { + getContainer().showPage(getNextPage()); + } else if (canFinishEarly()) { + if (getWizard().performFinish()) { + ((WizardDialog) getContainer()).close(); + } + } + } + + /** + * (non-Javadoc) Method declared on IDialogPage. + */ + public void createControl(Composite parent) { + IDialogSettings settings = getDialogSettings(); + newResourcePage = new NewRepositoryWizardNewPage(this, wizardCategories, primaryWizards, projectsOnly); + newResourcePage.setDialogSettings(settings); + + Control control = newResourcePage.createControl(parent); + getWorkbench().getHelpSystem().setHelp(control, IWorkbenchHelpContextIds.NEW_WIZARD_SELECTION_WIZARD_PAGE); + setControl(control); + } + + /** + * Since Finish was pressed, write widget values to the dialog store so that they will persist into the next + * invocation of this wizard page + */ + protected void saveWidgetValues() { + newResourcePage.saveWidgetValues(); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.wizard.IWizardPage#canFlipToNextPage() + */ + @Override + public boolean canFlipToNextPage() { + // if the current page advertises that it does have pages then ask it via the super call + if (hasPages) { + return super.canFlipToNextPage(); + } + return false; + } + + /** + * Sets whether the selected wizard advertises that it does provide pages. + * + * @param newValue + * whether the selected wizard has pages + * @since 3.1 + */ + public void setHasPages(boolean newValue) { + hasPages = newValue; + } + + /** + * Sets whether the selected wizard advertises that it can finish early. + * + * @param newValue + * whether the selected wizard can finish early + * @since 3.1 + */ + public void setCanFinishEarly(boolean newValue) { + canFinishEarly = newValue; + } + + /** + * Answers whether the currently selected page, if any, advertises that it may finish early. + * + * @return whether the page can finish early + * @since 3.1 + */ + public boolean canFinishEarly() { + return canFinishEarly; + } +} |

