// -*- mode: java; c-basic-offset: 2; -*-
// Copyright 2009-2011 Google, All Rights reserved
// Copyright 2011-2012 MIT, All rights reserved
// Released under the Apache License, Version 2.0
// http://www.apache.org/licenses/LICENSE-2.0
package com.google.appinventor.client.explorer.project;
import com.google.appinventor.client.Ode;
import static com.google.appinventor.client.Ode.MESSAGES;
import com.google.appinventor.client.OdeAsyncCallback;
import com.google.appinventor.client.settings.project.ProjectSettings;
import com.google.appinventor.shared.rpc.project.ProjectNode;
import com.google.appinventor.shared.rpc.project.ProjectRootNode;
import com.google.appinventor.shared.rpc.project.UserProject;
import java.util.ArrayList;
import java.util.List;
/**
* This class represents a project.
*
*/
public final class Project {
// Information about the project
private final UserProject projectInfo;
// List of listeners for any project changes.
private final List<ProjectChangeListener> projectChangeListeners;
private boolean loadingInProgress;
// Root project node
private ProjectRootNode projectRoot; // lazily and asynchronously initialized
// Project specific settings
private ProjectSettings settings; // lazily and asynchronously initialized
/**
* Creates a new project.
*
* @param projectInfo information about the project
*/
Project(UserProject projectInfo) {
this.projectInfo = projectInfo;
projectChangeListeners = new ArrayList<ProjectChangeListener>();
}
/*
* Loads the project's nodes from the backend.
*/
public void loadProjectNodes() {
if (projectRoot == null && !loadingInProgress) {
loadingInProgress = true;
Ode.CLog("Project.loadProjectNodes(): loadingInProgress = true");
if (settings == null) {
settings = new ProjectSettings(Project.this);
settings.loadSettings();
}
Ode.getInstance().getProjectService().getProject(
getProjectId(),
new OdeAsyncCallback<ProjectRootNode>(
// failure message
MESSAGES.projectLoadError()) {
@Override
public void onSuccess(ProjectRootNode result) {
projectRoot = result;
Ode.CLog("Project.loadProjectNodes(): loadingInProgress = false");
loadingInProgress = false;
fireProjectLoaded();
}
@Override
public void onFailure(Throwable caught) {
loadingInProgress = false;
super.onFailure(caught);
}
});
}
}
/**
* Returns the id of this project.
*
* @return project id
*/
public long getProjectId() {
return projectInfo.getProjectId();
}
/**
* Returns the id of this project's attribution.
*
* @return attribution id
*/
public long getAttributionId() {
return projectInfo.getAttributionId();
}
/**
* Returns the name of this project.
*
* @return project name
*/
public String getProjectName() {
return projectInfo.getProjectName();
}
/**
* Returns the type of this project.
*
* @return project type
*/
public String getProjectType() {
return projectInfo.getProjectType();
}
/**
* Returns the date of when the project was created.
*
* @return date created in milliseconds
*/
public long getDateCreated() {
return projectInfo.getDateCreated();
}
/**
* Returns the date of when the project was last modified.
*
* @return date modified in milliseconds
*/
public long getDateModified() {
return projectInfo.getDateModified();
}
/**
* Sets the date of when the project was last modified.
*
*/
public void setDateModified(long date) {
projectInfo.setDateModified(date);
}
public boolean isPublished() {
if (projectInfo.getGalleryId() <= UserProject.NOTPUBLISHED) {
/* The current unpublished project has galleryId == 0, but some old
* unpublished projects in database may still have -1 as unpublished value.
* Therefore, we use <= 0 to make sure this check.*/
return false;
}
return true;
}
public long getGalleryId() {
return projectInfo.getGalleryId();
}
public void setGalleryId(long id) {
projectInfo.setGalleryId(id);
}
/**
* Returns the project specific settings, or null if the settings haven't
* been loaded.
*
* @return project settings
*/
public ProjectSettings getSettings() {
return settings;
}
/**
* Returns the project's root node, or null if the project nodes haven't
* been loaded.
*
* @return project root node
*/
public ProjectRootNode getRootNode() {
return projectRoot;
}
/**
* Adds the given node to the project.
*
* <p/>If a node with the same file id already exists, the node is not added
* and the existing node is returned. However, the 'project node added' event
* is still fired.
*
* @param parent parent node of node to be added
* @param node node to be added
* @return the node that was added, or the existing node with the same file id
*/
public ProjectNode addNode(ProjectNode parent, ProjectNode node) {
boolean nodeAlreadyExists = false;
for (ProjectNode child : parent.getChildren()) {
if (child.getFileId().equals(node.getFileId())) {
nodeAlreadyExists = true;
node = child;
break;
}
}
if (!nodeAlreadyExists) {
parent.addChild(node);
}
// Event if the node already exists, we still call fireProjectNodeAdded so that asset property
// editors can detect that an asset was updated.
fireProjectNodeAdded(node);
return node;
}
/**
* Deletes the given node from the project.
*
* @param node node to be deleted
*/
public void deleteNode(ProjectNode node) {
ProjectNode parent = node.getParent();
if (parent != null) {
parent.removeChild(node);
}
fireProjectNodeRemoved(node);
}
/**
* Adds a {@link ProjectChangeListener} to the listener list.
*
* @param listener the {@code ProjectChangeListener} to be added
*/
public void addProjectChangeListener(ProjectChangeListener listener) {
projectChangeListeners.add(listener);
}
/**
* Removes a {@link ProjectChangeListener} from the listener list.
*
* @param listener the {@code ProjectChangeListener} to be removed
*/
public void removeProjectChangeListener(ProjectChangeListener listener) {
projectChangeListeners.remove(listener);
}
private List<ProjectChangeListener> copyProjectChangeListeners() {
return new ArrayList<ProjectChangeListener>(projectChangeListeners);
}
/*
* Triggers a 'project loaded' event to be sent to the listener on the listener list.
*/
private void fireProjectLoaded() {
for (ProjectChangeListener listener : copyProjectChangeListeners()) {
listener.onProjectLoaded(this);
}
}
/*
* Triggers a 'project node added' event to be sent to the listener on the listener list.
*/
private void fireProjectNodeAdded(ProjectNode node) {
for (ProjectChangeListener listener : copyProjectChangeListeners()) {
listener.onProjectNodeAdded(this, node);
}
}
/*
* Triggers a 'project node removed' event to be sent to the listener on the listener list.
*/
private void fireProjectNodeRemoved(ProjectNode node) {
for (ProjectChangeListener listener : copyProjectChangeListeners()) {
listener.onProjectNodeRemoved(this, node);
}
}
}