/*
* Copyright 2014 PRImA Research Lab, University of Salford, United Kingdom
*
* 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.primaresearch.web.gwt.client.page;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.primaresearch.dla.page.layout.physical.shared.LowLevelTextType;
import org.primaresearch.dla.page.layout.physical.shared.RegionType;
import org.primaresearch.shared.Pair;
import org.primaresearch.web.gwt.shared.page.ContentObjectC;
import org.primaresearch.web.gwt.shared.page.ContentObjectSync;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
/**
* Manager for synchronisation of page content between server and client.
*
* @author Christian Clausner
*
*/
public class PageSyncManager {
private String url;
private DocumentPageSyncServiceAsync syncService = GWT.create(DocumentPageSyncService.class);
private Set<PageSyncListener> listeners = new HashSet<PageSyncListener>(2);
private PageLayoutC pageLayout;
/**
* Constructor
*
* @param url URL of page file to sync with
* @param pageLayout Client side page layout object
*/
public PageSyncManager(String url, PageLayoutC pageLayout) {
this.url = url;
this.pageLayout = pageLayout;
}
/**
* Loads all page content objects of the specified type.
* @param contentType Content type (Region, TextLine, ...)
*/
public void loadContentObjectsAsync(final String contentType) {
AsyncCallback<ArrayList<ContentObjectC>> callback = new AsyncCallback<ArrayList<ContentObjectC>>() {
public void onFailure(Throwable caught) {
notifyListenersContentLoadingFailed(contentType, caught);
}
public void onSuccess(ArrayList<ContentObjectC> contentObjects) {
pageLayout.setContent(contentType, contentObjects);
notifyListenersContentLoaded(contentType);
}
};
syncService.loadContentObjects(url, contentType, callback);
}
/**
* Sends the text content of the given object to the server.
* @param object Text container content object
*/
public void syncTextContent(final ContentObjectC object) {
AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
notifyListenersTextContentSyncFailed(object, caught);
}
public void onSuccess(Boolean success) {
notifyListenersTextContentSynced(object);
}
};
syncService.putTextContent(url, object.getType(), object.getId(), object.getText(), callback);
}
/**
* Sends the text content of the given object to the server.
* @param object Text container content object
*/
public void syncRegionType(final ContentObjectC region, final RegionType newType, final String newSubType) {
if (!(region.getType() instanceof RegionType))
return;
AsyncCallback<Pair<ContentObjectC,ArrayList<String>>> callback = new AsyncCallback<Pair<ContentObjectC,ArrayList<String>>>() {
public void onFailure(Throwable caught) {
notifyListenersRegionTypeSyncFailed(region, caught);
}
public void onSuccess(Pair<ContentObjectC,ArrayList<String>> resultData) {
notifyListenersRegionTypeSynced(resultData);
}
};
syncService.setRegionType(url, (RegionType)region.getType(), newType, newSubType, region.getId(), callback);
}
/*public void loadMetaData() {
AsyncCallback<MetaData> callback = new AsyncCallback<MetaData>() {
public void onFailure(Throwable caught) {
}
public void onSuccess(MetaData metaData) {
pageLayout.setMetaData(metaData);
notifyListenersMetaDataLoaded();
}
};
syncService.loadMetaData(url, callback);
}*/
/**
* Requests a PAGE Ground Truth and Storage ID (GtsID) from the server.
*/
public void loadPageId() {
AsyncCallback<String> callback = new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
notifyListenersPageIdLoadingFailed(caught);
}
public void onSuccess(String id) {
pageLayout.setId(id);
notifyListenersPageIdLoaded(id);
}
};
syncService.getPageId(url, callback);
}
public void addListener(PageSyncListener listener) {
listeners.add(listener);
}
public void removeListener(PageSyncListener listener) {
listeners.remove(listener);
}
private void notifyListenersContentLoaded(String contentType) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().contentLoaded(contentType);
}
}
//private void notifyListenersMetaDataLoaded() {
// for (Iterator<PageLoadListener> it = listeners.iterator(); it.hasNext(); ) {
// it.next().metaDataLoaded();
// }
//}
private void notifyListenersPageIdLoaded(String id) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().pageIdLoaded(id);
}
}
private void notifyListenersContentLoadingFailed(String contentType, Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().contentLoadingFailed(contentType, caught);
}
}
private void notifyListenersPageIdLoadingFailed(Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().pageIdLoadingFailed(caught);
}
}
private void notifyListenersContentObjectAddingFailed(ContentObjectC object, Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().contentObjectAddingFailed(object, caught);
}
}
private void notifyListenersContentObjectDeletionFailed(ContentObjectC object, Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().contentObjectDeletionFailed(object, caught);
}
}
private void notifyListenersTextContentSyncFailed(ContentObjectC object, Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().textContentSyncFailed(object, caught);
}
}
private void notifyListenersRegionTypeSyncFailed(ContentObjectC object, Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().regionTypeSyncFailed(object, caught);
}
}
private void notifyListenersObjectOutlineSyncFailed(ContentObjectC object, Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().objectOutlineSyncFailed(object, caught);
}
}
private void notifyListenersPageFileSaveFailed(Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().pageFileSaveFailed(caught);
}
}
private void notifyListenersRevertChangesFailed(Throwable caught) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().revertChangesFailed(caught);
}
}
private void notifyListenersContentObjectAdded(ContentObjectSync syncObj, ContentObjectC localObj) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().contentObjectAdded(syncObj, localObj);
}
}
private void notifyListenersContentObjectDeleted(ContentObjectC obj) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().contentObjectDeleted(obj);
}
}
private void notifyListenersTextContentSynced(ContentObjectC obj) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().textContentSynchronized(obj);
}
}
private void notifyListenersRegionTypeSynced(Pair<ContentObjectC,ArrayList<String>> resultData) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
ContentObjectC obj = resultData != null ? resultData.left : null;
ArrayList<String> toDelete = resultData != null ? resultData.right : null;
it.next().regionTypeSynchronized(obj, toDelete);
}
}
private void notifyListenersObjectOutlineSynced(ContentObjectC obj) {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().objectOutlineSynchronized(obj);
}
}
private void notifyListenersPageFileSaved() {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().pageFileSaved();
}
}
private void notifyListenersChangesReverted() {
for (Iterator<PageSyncListener> it = listeners.iterator(); it.hasNext(); ) {
it.next().changesReverted();
}
}
/**
* Resets this manager.
*/
public void clear() {
url = null;
pageLayout.clear();
}
/**
* Sets the current PAGE XML source.
* @param url URL of PAGE file.
*/
public void setUrl(String url) {
this.url = url;
}
/**
* Adds the given content object (that has been created on client side)
* to the page layout on the server. Returns the same object enriched with
* new ID and attributes.<br>
* If the server returns <code>null</code> as object, the object could not
* be added to the page layout on server side and will be removed from the client page layout.
*/
public void addContentObject(final ContentObjectC object) {
AsyncCallback<ContentObjectSync> callback = new AsyncCallback<ContentObjectSync>() {
public void onFailure(Throwable caught) {
notifyListenersContentObjectAddingFailed(object, caught);
}
public void onSuccess(ContentObjectSync res) {
if (res != null) {
ContentObjectC obj = pageLayout.findContentObject(res.id);
if (obj != null) {
if (res.object != null) {
//Copy content
obj.setId(res.object.getId());
obj.setAttributes(res.object.getAttributes());
notifyListenersContentObjectAdded(res, obj);
}
else { //No object returned from server -> delete object on client
pageLayout.remove(obj);
String text = null;
if (LowLevelTextType.TextLine.equals(obj.getType()))
text = "The text line has not been created because there is no text region at this position.";
else if (LowLevelTextType.Word.equals(obj.getType()))
text = "The word has not been created because there is no text line at this position.";
else if (LowLevelTextType.Glyph.equals(obj.getType()))
text = "The glyph has not been created because there is no word at this position.";
if (text != null)
Window.alert(text);
}
}
}
}
};
syncService.addContentObject(url, object, callback);
}
/**
* Sends an updated object polygon to the server.
* @param object Page object with polygon.
*/
public void syncObjectOutline(final ContentObjectC object) {
AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
notifyListenersObjectOutlineSyncFailed(object, caught);
}
public void onSuccess(Boolean success) {
notifyListenersObjectOutlineSynced(object);
}
};
syncService.updateOutline(url, object.getType(), object.getId(), object.getCoords(), callback);
}
/**
* Sends a request to delete a page object on the server.
* @param object
*/
public void deleteContentObject(final ContentObjectC object) {
AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
notifyListenersContentObjectDeletionFailed(object, caught);
}
public void onSuccess(Boolean success) {
notifyListenersContentObjectDeleted(object);
}
};
syncService.deleteContentObject(url, object.getType(), object.getId(), callback);
}
/**
* Sends a request to save the current PAGE file permanently.
*/
public void save() {
AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
notifyListenersPageFileSaveFailed(caught);
}
public void onSuccess(Boolean success) {
notifyListenersPageFileSaved();
}
};
syncService.save(url, callback);
}
/**
* Sends a request to discard all changes since the initial load or the last save.
*/
public void revertChanges() {
AsyncCallback<Boolean> callback = new AsyncCallback<Boolean>() {
public void onFailure(Throwable caught) {
notifyListenersRevertChangesFailed(caught);
}
public void onSuccess(Boolean success) {
notifyListenersChangesReverted();
}
};
syncService.revertChanges(url, callback);
}
/**
* Listener interface for page content synchronisation between client and server.
*
* @author Christian Clausner
*
*/
public static interface PageSyncListener {
/** Called when the document page content (e.g. regions) has been loaded successfully */
public void contentLoaded(String contentType);
/** Called when loading the document page content (e.g. regions) has failed */
public void contentLoadingFailed(String contentType, Throwable caught);
//public void metaDataLoaded();
/** Called when the document page ID (ground truth and storage ID) has been loaded successfully */
public void pageIdLoaded(String id);
/** Called when loading the document page ID (ground truth and storage ID) has failed */
public void pageIdLoadingFailed(Throwable caught);
/** Called when the page content object (e.g. a region) has been added successfully on server side */
public void contentObjectAdded(ContentObjectSync syncObj, ContentObjectC localObj);
/** Called when the page content object (e.g. a region) could not be added on server side */
public void contentObjectAddingFailed(ContentObjectC object, Throwable caught);
/** Called when the page content object (e.g. a region) has been deleted successfully on server side */
public void contentObjectDeleted(ContentObjectC object);
/** Called when the page content object (e.g. a region) could not be deleted on server side */
public void contentObjectDeletionFailed(ContentObjectC object, Throwable caught);
/** Called when the text content has been sent successfully to the server */
public void textContentSynchronized(ContentObjectC object);
/** Called when the text content could not be synchronised to the server */
public void textContentSyncFailed(ContentObjectC object, Throwable caught);
/** Called when the region type change has been applied successfully on server side */
public void regionTypeSynchronized(ContentObjectC object, ArrayList<String> childObjectsToDelete);
/** Called when the region type change could not be applied on server side */
public void regionTypeSyncFailed(ContentObjectC object, Throwable caught);
/** Called when the object outline change has been applied successfully on server side */
public void objectOutlineSynchronized(ContentObjectC object);
/** Called when the object outline change could not be applied on server side */
public void objectOutlineSyncFailed(ContentObjectC object, Throwable caught);
/** Called when the page content has been saved successfully on server side */
public void pageFileSaved();
/** Called when the page content could not be saved on server side */
public void pageFileSaveFailed(Throwable caught);
/** Called when the revert has been executed successfully on server side */
public void changesReverted();
/** Called when the revert could not executed on server side */
public void revertChangesFailed(Throwable caught);
}
}