/**
* Copyright (c) 2014-2017 by the respective copyright holders.
* 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
*/
package org.eclipse.smarthome.core.thing.firmware;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Locale;
import org.eclipse.smarthome.core.events.Event;
import org.eclipse.smarthome.core.events.EventPublisher;
import org.eclipse.smarthome.core.i18n.I18nProvider;
import org.eclipse.smarthome.core.thing.ThingUID;
import org.eclipse.smarthome.core.thing.binding.firmware.FirmwareUID;
import org.eclipse.smarthome.core.thing.binding.firmware.FirmwareUpdateHandler;
import org.eclipse.smarthome.core.thing.binding.firmware.ProgressCallback;
import org.eclipse.smarthome.core.thing.binding.firmware.ProgressStep;
import org.osgi.framework.Bundle;
import org.osgi.framework.FrameworkUtil;
import com.google.common.base.Preconditions;
/**
* The callback implementation for the {@link ProgressCallback}.
*
* @author Thomas Höfer - Initial contribution
* @author Christoph Knauf - Introduced pending, canceled, update and InternalState
*/
final class ProgressCallbackImpl implements ProgressCallback {
private static String UPDATE_CANCELED_MESSAGE_KEY = "update-canceled";
/**
* Handler instance is needed to retrieve the error messages from the correct bundle.
*/
private final FirmwareUpdateHandler firmwareUpdateHandler;
private final EventPublisher eventPublisher;
private final I18nProvider i18nProvider;
private final ThingUID thingUID;
private final FirmwareUID firmwareUID;
private final Locale locale;
private Collection<ProgressStep> sequence;
private Iterator<ProgressStep> progressIterator;
private ProgressStep current;
private Integer progress;
private enum InternalState {
FINISHED,
PENDING,
RUNNING,
INITIALIZED
};
private InternalState state;
ProgressCallbackImpl(FirmwareUpdateHandler firmwareUpdateHandler, EventPublisher eventPublisher,
I18nProvider i18nProvider, ThingUID thingUID, FirmwareUID firmwareUID, Locale locale) {
this.firmwareUpdateHandler = firmwareUpdateHandler;
this.eventPublisher = eventPublisher;
this.i18nProvider = i18nProvider;
this.thingUID = thingUID;
this.firmwareUID = firmwareUID;
this.locale = locale;
this.progress = null;
}
@Override
public void defineSequence(ProgressStep... sequence) {
Preconditions.checkArgument(sequence != null && sequence.length > 0, "Sequence must not be null or empty.");
this.sequence = Collections.unmodifiableCollection(Arrays.asList(sequence));
progressIterator = this.sequence.iterator();
this.state = InternalState.INITIALIZED;
}
@Override
public void next() {
Preconditions.checkState(this.state != InternalState.FINISHED, "Update is finished.");
if (this.state == InternalState.PENDING) {
state = InternalState.RUNNING;
postProgressInfoEvent();
} else if (progressIterator.hasNext()) {
state = InternalState.RUNNING;
this.current = progressIterator.next();
postProgressInfoEvent();
} else {
state = InternalState.FINISHED;
throw new IllegalStateException("There is no further progress step to be executed.");
}
}
@Override
public void failed(String errorMessageKey, Object... arguments) {
Preconditions.checkState(this.state != InternalState.FINISHED, "Update is finished.");
Preconditions.checkArgument(errorMessageKey != null && !errorMessageKey.isEmpty(),
"The error message key must not be null or empty.");
this.state = InternalState.FINISHED;
String errorMessage = getMessage(firmwareUpdateHandler.getClass(), errorMessageKey, arguments);
postResultInfoEvent(FirmwareUpdateResult.ERROR, errorMessage);
}
@Override
public void success() {
Preconditions.checkState(this.state != InternalState.FINISHED, "Update is finished.");
Preconditions.checkState((this.progress != null && this.progress == 100) || (this.progressIterator!=null && !progressIterator.hasNext()),
"Update can't be successfully finished until progress is 100% or last progress step is reached");
this.state = InternalState.FINISHED;
postResultInfoEvent(FirmwareUpdateResult.SUCCESS, null);
}
@Override
public void pending() {
Preconditions.checkState(this.state != InternalState.FINISHED, "Update is finished.");
this.state = InternalState.PENDING;
postProgressInfoEvent();
}
@Override
public void canceled() {
Preconditions.checkState(this.state != InternalState.FINISHED, "Update is finished.");
this.state = InternalState.FINISHED;
String cancelMessage = getMessage(this.getClass(), UPDATE_CANCELED_MESSAGE_KEY);
postResultInfoEvent(FirmwareUpdateResult.CANCELED, cancelMessage);
}
@Override
public void update(int progress) {
Preconditions.checkState(this.state != InternalState.FINISHED, "Update is finished.");
Preconditions.checkArgument(progress >= 0 && progress <= 100, "The progress must be between 0 and 100.");
if (this.progress == null) {
updateProgress(progress);
} else if (progress < this.progress) {
throw new IllegalArgumentException("The new progress must not be smaller than the old progress.");
} else if (this.progress != progress) {
updateProgress(progress);
}
}
private void updateProgress(int progress) {
this.progress = progress;
this.state = InternalState.RUNNING;
postProgressInfoEvent();
}
void failedInternal(String errorMessageKey) {
this.state = InternalState.FINISHED;
String errorMessage = getMessage(ProgressCallbackImpl.class, errorMessageKey, new Object[] {});
postResultInfoEvent(FirmwareUpdateResult.ERROR, errorMessage);
}
private String getMessage(Class<?> clazz, String errorMessageKey, Object... arguments) {
Bundle bundle = FrameworkUtil.getBundle(clazz);
String errorMessage = i18nProvider.getText(bundle, errorMessageKey, null, locale, arguments);
return errorMessage;
}
private void postResultInfoEvent(FirmwareUpdateResult result, String message) {
post(FirmwareEventFactory.createFirmwareUpdateResultInfoEvent(new FirmwareUpdateResultInfo(result, message),
thingUID));
}
private void postProgressInfoEvent() {
if (this.progress == null) {
post(FirmwareEventFactory.createFirmwareUpdateProgressInfoEvent(new FirmwareUpdateProgressInfo(firmwareUID,
getCurrentStep(), sequence, this.state == InternalState.PENDING), thingUID));
} else {
post(FirmwareEventFactory.createFirmwareUpdateProgressInfoEvent(new FirmwareUpdateProgressInfo(firmwareUID,
getCurrentStep(), sequence, this.state == InternalState.PENDING, progress), thingUID));
}
}
private void post(Event event) {
eventPublisher.post(event);
}
private ProgressStep getCurrentStep() {
if (current != null) {
return current;
}
if (sequence != null && progressIterator.hasNext()) {
this.current = progressIterator.next();
return current;
}
return null;
}
}