// -*- 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.shared.rpc.project;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.google.common.base.Preconditions;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* Project nodes are used for the structural representation of a project. This
* is the superclass for all project nodes.
*
* <p>The node can directly represent files or folders in the filesystem on
* the backend, as well as 'virtual' files or folders. An example for a
* virtual folder is a package node which maps to a series of nested folders.
*
* <p>Project nodes will be returned by the backend via RPC to the client
* in the browser. Therefore they must be serializable. If there are any fields
* that cannot be serialized, they must be marked as transient.
*
*/
public abstract class ProjectNode implements Serializable, IsSerializable {
// For serialization
private static final long serialVersionUID = -6903337206811923033L;
// Name of project node (does not need to match the name of the file/folder it represents)
private String name;
// ID to identify the file/folder represented by the project node on the backend
private String fileId;
// Parent node of this node, {@code null} for the root project node
private ProjectNode parent;
// Children of this node (corresponds to files in folder on backend)
private List<ProjectNode> children;
/**
* Default constructor (for serialization only).
* Unfortunately this will prevent any fields from being marked as final!
*/
public ProjectNode() {
}
/**
* Creates a new project node.
*
* @param name project node name (can be different from file or folder
* represented by this node)
* @param fileId ID to identify the file/folder represented by the project
* node on the backend (can be {@code null} for 'virtual'
* folders)
*/
public ProjectNode(String name, String fileId) {
this.name = name;
this.fileId = fileId;
}
/**
* Adds another project node as a child to this project node.
*
* @param child new child to be added to this node
*/
public void addChild(ProjectNode child) {
if (children == null) {
children = new ArrayList<ProjectNode>();
}
children.add(child);
child.setParent(this);
}
/**
* Removes an existing child node from this project node.
*
* @param child child to be removed from this node
*/
public void removeChild(ProjectNode child) {
Preconditions.checkNotNull(children);
children.remove(child);
}
/**
* Sets a project node to be the parent of this project node.
*
* @param node parent node for this node
*/
protected void setParent(ProjectNode node) {
parent = node;
}
/**
* Returns the project node's parent.
*
* @return parent project node ({@code null} for root node)
*/
public ProjectNode getParent() {
return parent;
}
/**
* Returns the root project node for this project node. This method can be
* called on any project node within the project hierarchy.
*
* @return root node of the associated project
*/
public ProjectRootNode getProjectRoot() {
return parent.getProjectRoot();
}
/**
* Returns the ID of the project associated with this node. This method can be
* called on any project node within the project hierarchy.
*
* @return ID of the associated project
*/
public long getProjectId() {
return getProjectRoot().getProjectId();
}
/**
* Returns the project type of the nodes in this project (all nodes within
* a project share the same project type).
*
* <p>Note that {@link ProjectRootNode} subclasses need to override this
* method!
*
* @return type of the associated project
*/
public String getProjectType() {
return getProjectRoot().getProjectType();
}
/**
* Returns the name of this project node.
*
* @return name of this node (doesn't have to be the same as the name of
* the file/folder represented by this node)
*/
public String getName() {
return name;
}
/**
* Returns the full name of this project node.
*
* @return full name of this node
*/
public String getFullName() {
// The fileId is the full name of the node.
return fileId;
}
/**
* Returns the ID for this project node.
*
* @return ID to identify the file/folder represented by the project node
* on the backend (can be {@code null} for 'virtual' folders)
*/
public String getFileId() {
return fileId;
}
/**
* Returns an iterable for the children of this node.
*
* @return iterable
*/
public Iterable<ProjectNode> getChildren() {
List<ProjectNode> result = children;
if (result == null) {
result = Collections.emptyList();
}
return result;
}
/**
* Indicates whether a node is a source node.
*
* @return {@code true} for source nodes, {@code false} otherwise
*/
protected boolean isSourceNode() {
return false;
}
/**
* Recursively looks for a project node with the given file ID.
*
* @param fileId file ID to look for
* @return found project node or {@code null}
*/
public ProjectNode findNode(String fileId) {
if (fileId.equals(getFileId())) {
return this;
}
if (children != null) {
for (ProjectNode child : children) {
ProjectNode found = child.findNode(fileId);
if (found != null) {
return found;
}
}
}
return null;
}
/**
* Recursively looks for a project node with the given type.
*
* @param type class of node to look for
* @return found project node or {@code null}
*/
public ProjectNode findNode(Class<?> type) {
if (getClass().equals(type)) {
return this;
}
if (children != null) {
for (ProjectNode child : children) {
ProjectNode found = child.findNode(type);
if (found != null) {
return found;
}
}
}
return null;
}
/**
* Recursively collects all source nodes.
*
* @param bucket container to collect found source nodes in
*/
protected void findSourceNodes(List<ProjectNode> bucket) {
if (children != null) {
for (ProjectNode child : children) {
if (child.isSourceNode()) {
bucket.add(child);
}
child.findSourceNodes(bucket);
}
}
}
/**
* Used to rename a node.
*
* <p>Note that this will not cause a rename in the storage system!
*
* @param newName new name
*/
public void setName(String newName) {
name = newName;
}
}