org.icefaces.ace.component.fileentry.FileEntry Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of icefaces-ace Show documentation
Show all versions of icefaces-ace Show documentation
${icefaces.product.name} ACE Component Library
/*
* Copyright 2004-2014 ICEsoft Technologies Canada Corp.
*
* 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 org.icefaces.ace.component.fileentry;
import org.icefaces.ace.component.clientValidator.Validateable;
import org.icefaces.ace.util.JSONBuilder;
import org.icefaces.ace.util.Utils;
import org.icefaces.component.Focusable;
import org.icefaces.impl.event.BridgeSetup;
import org.icefaces.util.JavaScriptRunner;
import javax.el.ValueExpression;
import javax.faces.application.FacesMessage;
import javax.faces.application.ProjectStage;
import javax.faces.context.FacesContext;
import javax.faces.event.FacesEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.AbortProcessingException;
import javax.faces.component.UIForm;
import javax.el.ELContext;
import javax.el.ELException;
import javax.el.MethodExpression;
import java.util.Map;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
public class FileEntry extends FileEntryBase implements Focusable, Validateable {
private static Logger log = Logger.getLogger(FileEntry.class.getName());
private static final String RESULTS_KEY = "org.icefaces.ace.component.fileEntry.results";
private static final String EVENT_KEY = "org.icefaces.ace.component.fileEntry.events";
public FileEntry() {
super();
}
public void setResults(FileEntryResults results) {
try {
super.setResults(results);
}
catch(RuntimeException e) {
FacesContext facesContext = FacesContext.getCurrentInstance();
if (facesContext.isProjectStage(ProjectStage.Development) ||
facesContext.isProjectStage(ProjectStage.UnitTest)) {
log.log(Level.WARNING, "Problem setting results property on " +
"FileEntry component", e);
}
throw e;
}
}
public void reset() {
setResults(null);
}
public String getCallbackEL() {
javax.el.ValueExpression callbackExpression = getValueExpression("callback");
return callbackExpression == null ? null : callbackExpression.getExpressionString();
}
public boolean isViaCallback() {
return getCallbackEL() != null;
}
public String getProgressResourceName(FacesContext context) {
if (PushUtils.isPushPresent()) {
UIForm form = Utils.findParentForm(this);
return PushUtils.getProgressResourceName(context, form);
} else {
return null;
}
}
public String getProgressGroupName(FacesContext context) {
if (PushUtils.isPushPresent()) {
UIForm form = Utils.findParentForm(this);
return PushUtils.getPushGroupName(context, form);
} else {
return null;
}
}
/**
* Used by FileEntryPhaseListener, before the component treee exists, to
* save the results of the file uploads, to be retrieved later in the same
* lifecycle, once the component tree is in place.
*/
static void storeResultsForLaterInLifecycle(
FacesContext facesContext,
Map clientId2Results) {
facesContext.getAttributes().put(RESULTS_KEY, clientId2Results);
}
/**
* Used by FileEntryRenderer.decode(-) to retrieve each fileEntry
* component's results for file uploads.
*/
static FileEntryResults retrieveResultsFromEarlierInLifecycle(
FacesContext facesContext, String clientId) {
FileEntryResults results = null;
Map clientId2Result =
(Map) facesContext.getAttributes().get(
RESULTS_KEY);
if (clientId2Result != null) {
results = clientId2Result.get(clientId);
}
return results;
}
/**
* After the ApplyRequestValues phase, when the fileEntry components
* have all retrieved their results for uploaded files, clear the
* results away, so we don't leak memory.
*/
static void removeResults(FacesContext facesContext) {
facesContext.getAttributes().remove(RESULTS_KEY);
}
/**
* Invoked by processDecodes(FacesContext) or processValidators(FacesContext)
*/
protected void validateResults(FacesContext facesContext) {
log.finer("FileEntry.validateResults() clientId: " + getClientId(facesContext));
// The current lifecycle results. If no files were uploaded
// this lifecycle, then this is null. Different from getResults(),
// which may be from a previous lifecycle.
FileEntryResults results = retrieveResultsFromEarlierInLifecycle(
facesContext, getClientId(facesContext));
boolean failed = false;
if (results != null) {
for(FileEntryResults.FileInfo fi : results.getFiles()) {
if (!fi.isSaved()) {
log.finer("FileEntry.validateResults() FAILED file: " + fi);
failed = true;
break;
}
}
}
else {
// No files uploaded this lifecycle
// If required then failed unless was partial submit
String partialSubmitValue = facesContext.getExternalContext().
getRequestParameterMap().get("ice.submit.partial");
boolean partialSubmit = "true".equals(partialSubmitValue);
log.finer("FileEntry.validateResults() partialSubmit: " + partialSubmit + " required: " + isRequired());
if (!partialSubmit && isRequired()) {
if (isMessagePersistence()) {
addMessageFromRequired(facesContext);
}
failed = true;
log.finer("FileEntry.validateResults() FAILED required");
}
}
if (failed) {
facesContext.validationFailed();
facesContext.renderResponse();
}
}
protected void addMessagesFromResults(FacesContext facesContext) {
String clientId = getClientId(facesContext);
FileEntryResults results = getResults();
log.finer("FileEntry.addMessagesFromResults clientId: " + clientId + " results: " + results);
if (results != null) {
ArrayList files = results.getFiles();
for (FileEntryResults.FileInfo fi : files) {
FileEntryStatus status = fi.getStatus();
FacesMessage fm = status.getFacesMessage(facesContext, this, fi);
log.finer(
"FileEntry.addMessagesFromResults\n" +
" FileInfo: " + fi + "\n" +
" FacesMessage: " + fm);
facesContext.addMessage(clientId, fm);
}
}
}
protected void addMessageFromRequired(FacesContext facesContext) {
String clientId = getClientId(facesContext);
FacesMessage fm = FileEntryStatuses.REQUIRED.getFacesMessage(
facesContext, this, null);
log.finer("FileEntry.addMessageFromRequired clientId: " + clientId + " FacesMessage: " + fm);
facesContext.addMessage(clientId, fm);
}
/**
* @return The label property, if specified, else the clientId
*/
public String getFacesMessageLabel() {
String label = getLabel();
if (label != null && label.length() > 0) {
return label;
}
return getClientId();
}
private boolean isResetValidRequest(FacesContext facesContext) {
java.util.Map requestMap = facesContext.getExternalContext().getRequestParameterMap();
return (requestMap.get(getClientId(facesContext)+"_resetValid") != null);
}
@Override
/**
* Override to add the constraint that when immediate is true, then
* immediateValidation must be true as well, since validation must happen
* before the fileEntryListener is invoked.
*
* @see FileEntryBase#isImmediateValidation
*/
public boolean isImmediateValidation() {
return isImmediate() ? true : super.isImmediateValidation();
}
@Override
public void processDecodes(FacesContext facesContext) {
if (isResetValidRequest(facesContext)) return;
super.processDecodes(facesContext);
if (isImmediateValidation()) {
validateResults(facesContext);
}
}
@Override
public void processValidators(FacesContext facesContext) {
if (isResetValidRequest(facesContext)) return;
super.processValidators(facesContext);
if (!isImmediateValidation()) {
validateResults(facesContext);
}
}
@Override
public void queueEvent(FacesEvent event) {
log.finer("FileEntry.queueEvent clientId: " + getClientId());
if (isImmediate()) {
event.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
log.finer("FileEntry.queueEvent immediate == true queuing event: " + event);
super.queueEvent(event);
}
else {
event.setPhaseId(PhaseId.RENDER_RESPONSE);
log.finer("FileEntry.queueEvent immediate == false storing event: " + event);
storeEventForPreRender(event);
}
}
@Override
public void broadcast(FacesEvent event) {
log.finer("FileEntry.broadcast clientId: " + getClientId() + " event: " + event + " phaseId: " + event.getPhaseId());
if (event instanceof FileEntryEvent) {
FileEntryEvent fee = (FileEntryEvent) event;
FacesContext context = FacesContext.getCurrentInstance();
try {
log.finer("FileEntry.broadcast invoke: " + fee.isInvoke());
if (fee.isInvoke()) {
MethodExpression listener = getFileEntryListener();
if (listener != null) {
ELContext elContext = context.getELContext();
try {
Object result = listener.invoke(elContext, new Object[] {event});
log.finer("FileEntry.broadcast result: " + result);
if (result != null) {
String outcome = result.toString();
context.getApplication().getNavigationHandler().handleNavigation(
context,
(null != listener) ? listener.getExpressionString() : null,
outcome);
context.renderResponse();
return;
}
} catch (ELException ee) {
throw new AbortProcessingException(ee.getMessage(),
ee.getCause());
}
}
// If every file succeeded uploading, and the lifecycle is
// valid, then clear the file selection in the browser
if (getResults().isLifecycleAndUploadsSuccessful(context)){
String script = "ice.ace.fileentry.clearFileSelection(\"" +
JSONBuilder.escapeString(getClientId(context))+
"\")";
JavaScriptRunner.runScript(context, script);
}
}
} finally {
// ICE-5750 deals with re-adding faces messages for components
// that have not re-executed. Components that are executing
// should re-add their faces messages themselves.
// FileEntry will only re-add faces messages past the upload
// lifecycle if its messagePersistence property is true.
if (isMessagePersistence() || fee.isInvoke()) {
addMessagesFromResults(context);
}
}
}
else {
super.broadcast(event);
}
}
/**
* Used by FileEntry.queueEvent(-), to save non-immediate FileEntryEvent
* objects to be invoked by FileEntryPhaseListener in pre-Render phase.
*/
private void storeEventForPreRender(FacesEvent event) {
FacesContext facesContext = FacesContext.getCurrentInstance();
Map clientId2FacesEvent = (Map)
facesContext.getAttributes().get(EVENT_KEY);
if (clientId2FacesEvent == null) {
clientId2FacesEvent = new HashMap(6);
facesContext.getAttributes().put(EVENT_KEY, clientId2FacesEvent);
}
clientId2FacesEvent.put(getClientId(facesContext), event);
}
/**
* Used by FileEntryPhaseListener(-) to retrieve each the FileEntryEvent
* objects, so they can be invoked pre-Render phase.
*/
static Map removeEventsForPreRender(
FacesContext facesContext) {
Map clientId2FacesEvent = (Map)
facesContext.getAttributes().remove(EVENT_KEY);
return clientId2FacesEvent;
}
public String getFocusedElementId() {
return getClientId();
}
public String getValidatedElementId() {
return getClientId();
}
}