All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.guise.framework.component.PlatformFileUploadPanel Maven / Gradle / Ivy

There is a newer version: 0.5.3
Show newest version
/*
 * Copyright © 2005-2008 GlobalMentor, Inc. 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.guise.framework.component;

import java.net.URI;
import java.util.Collection;

import com.globalmentor.java.Objects;
import com.globalmentor.model.TaskState;
import com.globalmentor.si.*;

import io.guise.framework.Bookmark;
import io.guise.framework.Resources;
import io.guise.framework.component.layout.*;
import io.guise.framework.event.*;
import io.guise.framework.geometry.*;
import io.guise.framework.platform.*;
import io.guise.framework.prototype.AbstractActionPrototype;
import io.guise.framework.prototype.ActionPrototype;

import static com.globalmentor.java.Characters.*;
import static com.globalmentor.java.Classes.*;
import static com.globalmentor.net.URIs.*;
import static io.guise.framework.theme.Theme.*;

/**
 * Panel to browse platform files and upload them to the specified destination. Progress events are sent both for individual platform file transfer progress and
 * for overall progress. For the former, the source of the event will be the relevant {@link PlatformFile}; for the latter, the source of the event will be a
 * {@link PlatformFileUploadTask}.
 * @author Garret Wilson
 */
public class PlatformFileUploadPanel extends AbstractPanel implements ProgressListenable {

	/** The bound property of the destination URI. */
	public static final String DESTINATION_URI_PROPERTY = getPropertyName(PlatformFileUploadPanel.class, "destinationURI");

	/** The bound property of the destination bookmark. */
	public static final String DESTINATION_BOOKMARK_PROPERTY = getPropertyName(PlatformFileUploadPanel.class, "destinationBookmark");

	/** The number of platform files to display at the same time. */
	private static final int PLATFORM_FILE_DISPLAY_COUNT = 16;

	/** The panel containing controls such as buttons. */
	private final Panel controlPanel;

	/** @return The panel containing controls such as buttons. */
	public Panel getControlPanel() {
		return controlPanel;
	}

	/**
	 * The collection URI representing the base destination of the platform files, either absolute or relative to the application, or null if the
	 * destination URI has not yet been set.
	 */
	private URI destinationURI = null;

	/**
	 * @return The collection URI representing the base destination of the platform files, either absolute or relative to the application, or null if
	 *         the destination URI has not yet been set.
	 */
	public URI getDestinationURI() {
		return destinationURI;
	}

	/**
	 * Sets the destination base URI of the upload. This is a bound property.
	 * @param newDestinationURI The collection URI representing the base destination of the platform files, either absolute or relative to the application.
	 * @throws NullPointerException if the given URI is null.
	 * @throws IllegalArgumentException if the provided URI is not a collection URI.
	 * @throws IllegalArgumentException if the provided URI specifies a query and/or fragment.
	 * @see #DESTINATION_URI_PROPERTY
	 */
	public void setDestinationURI(final URI newDestinationURI) {
		if(!Objects.equals(destinationURI, checkCollectionURI(checkPlainURI(newDestinationURI)))) { //if the value is really changing
			final URI oldDestinationPath = destinationURI; //get the old value
			destinationURI = newDestinationURI; //actually change the value
			firePropertyChange(DESTINATION_URI_PROPERTY, oldDestinationPath, newDestinationURI); //indicate that the value changed
		}
	}

	/** The bookmark to be used in sending resources to the destination URI, or null if there is no bookmark specified. */
	private Bookmark destinationBookmark = null;

	/** @return The bookmark to be used in sending resources to the destination URI, or null if there is no bookmark specified. */
	public Bookmark getDestinationBookmark() {
		return destinationBookmark;
	}

	/**
	 * Sets the destination bookmark of the upload. This is a bound property.
	 * @param newDestinationBookmark The bookmark to be used in sending resources to the destination URI, or null if there is no bookmark specified.
	 * @see #DESTINATION_BOOKMARK_PROPERTY
	 */
	public void setDestinationBookmark(final Bookmark newDestinationBookmark) {
		if(!Objects.equals(destinationBookmark, newDestinationBookmark)) { //if the value is really changing
			final Bookmark oldDestinationBookmark = destinationBookmark; //get the old value
			destinationBookmark = newDestinationBookmark; //actually change the value
			firePropertyChange(DESTINATION_BOOKMARK_PROPERTY, oldDestinationBookmark, newDestinationBookmark); //indicate that the value changed
		}
	}

	/** The platform file list control. */
	private final ListControl platformFileListControl;

	/** The label containing the status of the current platform file. */
	private final Label platformFileStatusLabel;

	/** The label containing the overall status. */
	private final Label overallStatusLabel;

	/** The resource collect control. */
	//TODO del	private final ResourceCollectControl resourceCollectControl;

	/** @return The resource collect control. */
	//TODO del		public ResourceCollectControl getResourceCollectControl() {return resourceCollectControl;}

	/** The task for performing a file upload, or null if a file upload is not occuring. */
	private PlatformFileUploadTask platformFileUploadTask = null;

	/** The action prototype for browsing the platform file system. */
	private final ActionPrototype browseActionPrototype;

	/** @return The action prototype for browsing the platforom file system. */
	public ActionPrototype getBrowseActionPrototype() {
		return browseActionPrototype;
	}

	/** The action prototype for uploading. */
	private final ActionPrototype uploadActionPrototype;

	/** @return The action prototype for uploading. */
	public ActionPrototype getUploadActionPrototype() {
		return uploadActionPrototype;
	}

	/** The action prototype for canceling. */
	private final ActionPrototype cancelActionPrototype;

	/** @return The action prototype for canceling. */
	public ActionPrototype getCancelActionPrototype() {
		return cancelActionPrototype;
	}

	/** The progress listener that updates the platform file status label in response to individual platform file transfers. */
	private final ProgressListener platformFileProgressListener = new ProgressListener() {

		public void progressed(final ProgressEvent progressEvent) { //when progress occurs
			if(progressEvent.getTaskState() == TaskState.COMPLETE) { //if this file completes the upload
				platformFileListControl.remove(progressEvent.getSource()); //remove this platform file from the list
			}
			updatePlatformFileStatusLabel((PlatformFile)progressEvent.getSource(), progressEvent.getTaskState(), progressEvent.getProgress(),
					progressEvent.getCompletion()); //update the individual platform file status label with the progress
			fireProgressed(progressEvent); //pass along the progress event unmodified
		}
	};

	/** The progress listener that updates the overall status label in response to the overall platform file transfer task. */
	private final ProgressListener overallProgressListener = new ProgressListener() {

		@Override
		public void progressed(final ProgressEvent progressEvent) { //when progress occurs
			final TaskState state = progressEvent.getTaskState(); //get the current overall transfer status
			if(state == TaskState.COMPLETE || state == TaskState.CANCELED || state == TaskState.ERROR) { //if the overall transfer ends
				platformFileUploadTask.removeProgressListener(this); //stop listening for the overall progress
				platformFileUploadTask = null; //remove the upload task
			}
			updateOverallStatusLabel(state, progressEvent.getProgress(), progressEvent.getCompletion()); //update the overall status label with the progress
			updateComponents(); //update the components							
			fireProgressed(progressEvent); //pass along the progress event unmodified
		}

	};

	/**
	 * Destination URI constructor.
	 * @param destinationURI The collection URI representing the base destination of the platform files, either absolute or relative to the application.
	 * @throws NullPointerException if the given list of platform files and/or destination URI is null.
	 * @throws IllegalArgumentException if the provided URI is not a collection URI.
	 * @throws IllegalArgumentException if the provided URI specifies a query and/or fragment.
	 */
	public PlatformFileUploadPanel(final URI destinationURI) {
		this(destinationURI, null); //construct the panel with no bookmark
	}

	/**
	 * Destination URI and destination bookmark constructor.
	 * @param destinationURI The collection URI representing the base destination of the platform files, either absolute or relative to the application.
	 * @param destinationBookmark The bookmark to be used in sending resources to the destination URI, or null if there is no bookmark specified.
	 * @throws NullPointerException if the given list of platform files and/or destination URI is null.
	 * @throws IllegalArgumentException if the provided URI is not a collection URI.
	 * @throws IllegalArgumentException if the provided URI specifies a query and/or fragment.
	 */
	public PlatformFileUploadPanel(final URI destinationURI, final Bookmark destinationBookmark) {
		this(); //construct the default panel
		setDestinationURI(destinationURI); //set the destination URI
		setDestinationBookmark(destinationBookmark); //set the destionation bookmark
	}

	/** Default constructor with a default vertical flow layout. */
	public PlatformFileUploadPanel() {
		super(new FlowLayout(Flow.PAGE)); //construct the parent class
		platformFileListControl = new ListControl(PlatformFile.class, PLATFORM_FILE_DISPLAY_COUNT); //create a list in which to show the platform files
		//TODO del if not needed; bring back if we make the list control editable		platformFileListControl.setEditable(false);	//don't allow the list to be edited
		platformFileListControl.setLineExtent(new Extent(30, Unit.EM));
		add(platformFileListControl);

		platformFileStatusLabel = new Label(); //current status label
		add(platformFileStatusLabel);
		overallStatusLabel = new Label(); //overall status label
		add(overallStatusLabel);

		//the horizontal panel of controls
		controlPanel = new LayoutPanel(new FlowLayout(Flow.LINE));
		//TODO del		resourceCollectControl=new ResourceCollectControl();	//resource collector
		//TODO del		controlPanel.add(resourceCollectControl);

		browseActionPrototype = new AbstractActionPrototype(LABEL_BROWSE + HORIZONTAL_ELLIPSIS_CHAR, GLYPH_BROWSE) { //browse

			@Override
			protected void action(final int force, final int option) {
				getSession().getPlatform().selectPlatformFiles(true, new ValueSelectListener>() { //select platform files, listening for the selection to occur

					@Override
					public void valueSelected(final ValueEvent> valueEvent) { //when files are selected
						final Collection platformFiles = valueEvent.getValue(); //get the new platform files
						platformFileListControl.clear(); //remove the currently displayed platform files
						platformFileListControl.addAll(platformFiles); //add all the new platform files to the list
						for(final PlatformFile platformFile : platformFiles) { //for each platform file
							platformFile.removeProgressListener(platformFileProgressListener); //make sure we're not already listening for progress on this platform file
							platformFile.addProgressListener(platformFileProgressListener); //start listening for progress on this platform file
						}
						updateComponents(); //update the components
					}

				});
			}
		};
		controlPanel.add(browseActionPrototype);
		uploadActionPrototype = new AbstractActionPrototype(LABEL_UPLOAD, GLYPH_UPLOAD) { //resource upload

			@Override
			protected void action(final int force, final int option) {
				platformFileUploadTask = new PlatformFileUploadTask(platformFileListControl, getDestinationURI(), getDestinationBookmark()); //create a new platform file upload task
				platformFileUploadTask.addProgressListener(overallProgressListener); //listen for progress of the platform file upload task
				platformFileUploadTask.start(); //start the file uploads
				updateComponents(); //update the components to show the new state
			}
		};
		uploadActionPrototype.setEnabled(false); //initially disable upload
		controlPanel.add(uploadActionPrototype);
		cancelActionPrototype = new AbstractActionPrototype(LABEL_CANCEL, GLYPH_CANCEL) { //upload cancel

			@Override
			protected void action(final int force, final int option) {
				final PlatformFileUploadTask platformFileUploadTask = PlatformFileUploadPanel.this.platformFileUploadTask; //get the file platform upload task
				if(platformFileUploadTask != null) { //if there is an upload task
					platformFileUploadTask.cancel(); //cancel the upload task
					updateComponents(); //update the components to show the new state
				}
			}
		};
		cancelActionPrototype.setEnabled(false); //initially disable canceling
		controlPanel.add(cancelActionPrototype);

		//listen for the resource collection control changing its list of collected resource paths
		/*TODO del all resource collect control references
				resourceCollectControl.addPropertyChangeListener(ResourceCollectControl.RESOURCE_PATHS_PROPERTY, new AbstractGenericPropertyChangeListener>()
						{
							public void propertyChange(final GenericPropertyChangeEvent> genericPropertyChangeEvent) {	//if the list of resource path changes
								platformFileListControl.clear();	//remove the currently displayed resource paths
								platformFileListControl.addAll(genericPropertyChangeEvent.getNewValue());	//add all the new resource paths to the list
								updateComponents();	//update the components in response
							}
						});
		*/
		//listen for the resource collection control changing its send state, and update the state of the components in response
		/*TODO del
				resourceCollectControl.addPropertyChangeListener(ResourceCollectControl.STATE_PROPERTY, new AbstractGenericPropertyChangeListener()
						{
							public void propertyChange(final GenericPropertyChangeEvent propertyChangeEvent) {	//if the transfer state changes
								updateComponents();	//update the components in response
								updateStatusLabel(null, -1, propertyChangeEvent.getNewValue());	//update the status label with the new state
							}
						});
		*/
		//listen for progress from the resource collect control and update the progress labels in response
		/*TODO del
				resourceCollectControl.addProgressListener(new ProgressListener()
						{
							public void progressed(final ProgressEvent progressEvent) {	//if progress occurs
								updateStatusLabel(progressEvent.getTask(), progressEvent.getValue(), progressEvent.getTaskState());	//update the status level with the progress
								fireProgressed(new ProgressEvent(UploadPanel.this, progressEvent));	//refire the progress event using this panel as the source
							}
						});
		*/
		add(controlPanel);
	}

	/** Updates the state of components. */
	protected void updateComponents() {
		uploadActionPrototype.setEnabled(platformFileUploadTask == null && !platformFileListControl.isEmpty()); //only allow upload if no platform files are being uploaded and there are platform files to upload
		cancelActionPrototype.setEnabled(platformFileUploadTask != null && platformFileUploadTask.getState() == TaskState.INCOMPLETE); //only allow cancel if the there is an incomplete upload task
	}

	/**
	 * Updates the status label for an individual platform file.
	 * @param platformFile The current platform file.
	 * @param state The new transfer state, or null if there is no state.
	 * @param progress The current number of bytes transferred, or null if the bytes transferred is not known.
	 * @param completion The total number of bytes to transfer, or null if the total is not known.
	 * @throws NullPointerException if the given platform file is null.
	 */
	protected void updatePlatformFileStatusLabel(final PlatformFile platformFile, final TaskState state, final Long progress, final Long completion) {
		final StringBuilder statusStringBuilder = new StringBuilder(); //build the status string
		statusStringBuilder.append(platformFile.getName()).append(':').append(' '); //platform file:
		if(state == null || state == TaskState.INCOMPLETE) { //if we don't have a state or we have a normal progres state
			if(progress != null) { //if a valid value is given
				statusStringBuilder.append(SIUnit.BYTE.format(progress.longValue(), SIPrefix.KILO)); //show the value
			} else { //if there is no value
				statusStringBuilder.append(LABEL_UNKNOWN); //indicate an unknown progress
			}
			if(completion != null) { //if the total is known
				statusStringBuilder.append(" / ").append(SIUnit.BYTE.format(completion.longValue(), SIPrefix.KILO)); //show the total
			}
		} else if(state != null) { //if we're not transferring, just show the task state
			statusStringBuilder.append(Resources.getLabelResourceReference(state)); //show the task status label
		}
		platformFileStatusLabel.setLabel(statusStringBuilder.toString()); //update the status
	}

	/**
	 * Updates the status label for the overall progress.
	 * @param state The new transfer state, or null if there is no state.
	 * @param progress The current number of bytes transferred, or null if the bytes transferred is not known.
	 * @param completion The total number of bytes to transfer, or null if the total is not known.
	 */
	protected void updateOverallStatusLabel(final TaskState state, final Long progress, final Long completion) {
		final StringBuilder statusStringBuilder = new StringBuilder(); //build the status string TODO combine common code
		statusStringBuilder.append(LABEL_TOTAL).append(':').append(' '); //total:
		if(state == null || state == TaskState.INCOMPLETE) { //if we don't have a state or we have a normal progres state
			if(progress != null) { //if a valid value is given
				statusStringBuilder.append(SIUnit.BYTE.format(progress.longValue(), SIPrefix.KILO)); //show the value
			} else { //if there is no value
				statusStringBuilder.append(LABEL_UNKNOWN); //indicate an unknown progress
			}
			if(completion != null) { //if the total is known
				statusStringBuilder.append(" / ").append(SIUnit.BYTE.format(completion.longValue(), SIPrefix.KILO)); //show the total
			}
		} else if(state != null) { //if we're not transferring, just show the task state
			statusStringBuilder.append(Resources.getLabelResourceReference(state)); //show the task status label
		}
		overallStatusLabel.setLabel(statusStringBuilder.toString()); //update the status
	}

	@Override
	public void addProgressListener(final ProgressListener progressListener) {
		getEventListenerManager().add(ProgressListener.class, progressListener); //add the listener
	}

	@Override
	public void removeProgressListener(final ProgressListener progressListener) {
		getEventListenerManager().remove(ProgressListener.class, progressListener); //remove the listener
	}

	/**
	 * Fires a given progress event to all registered progress listeners.
	 * @param progressEvent The progress event to fire.
	 */
	protected void fireProgressed(final ProgressEvent progressEvent) {
		for(final ProgressListener progressListener : getEventListenerManager().getListeners(ProgressListener.class)) { //for each progress listener
			progressListener.progressed(progressEvent); //dispatch the progress event to the listener
		}
	}

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy