/**
* Copyright (c) 2002-2013 "Neo Technology,"
* Network Engine for Objects in Lund AB [http://neotechnology.com]
*
* This file is part of Neo4j.
*
* Neo4j is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.noser.neo4j.android.dbinspector.implementation;
import java.util.ArrayList;
import java.util.List;
import org.neo4j.android.client.GraphDatabase;
import org.neo4j.android.client.Neo4jServiceException;
import org.neo4j.android.client.NodeIterator;
import org.neo4j.android.common.Direction;
import org.neo4j.android.common.ParcelableNode;
import org.neo4j.android.common.ParcelableRelationship;
import roboguice.activity.RoboActivity;
import roboguice.inject.ContentView;
import roboguice.inject.InjectResource;
import roboguice.inject.InjectView;
import roboguice.util.Ln;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.os.RemoteException;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import com.google.inject.Inject;
import com.noser.neo4j.android.dbinspector.R;
import com.noser.neo4j.android.dbinspector.base.DBInspectorConstants;
import com.noser.neo4j.android.dbinspector.interfaces.IDBManager;
@ContentView(R.layout.rootnode_activity)
public class RootNodeActivity extends RoboActivity {
@Inject
private Context context;
@Inject
private IDBManager dbManager;
@InjectView(R.id.referenceNodeLayout)
private LinearLayout referenceNodeLayout;
@InjectView(R.id.rootNodesLayout)
private LinearLayout rootNodesLayout;
@InjectView(R.id.cycleNodesLayout)
private LinearLayout cycleNodesLayout;
@InjectView(R.id.nodeCreateButton)
ImageButton nodeCreateButton;
@Inject
private LayoutInflater inflater;
@InjectResource(R.string.node_create_default_relationship_name)
private String defaultRelationshipName;
private long referenceNodeId;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(getResources().getString(R.string.database_title) + " " + dbManager.getCurrentNeo4jDatabaseName());
retrieveReferenceNodeId();
nodeCreateButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
askCreateNode();
}
});
}
@Override
protected void onResume() {
super.onResume();
updateNodesList();
}
private void retrieveReferenceNodeId() {
try {
GraphDatabase database = dbManager.getCurrentNeo4jDatabase();
database.beginTx();
try {
ParcelableNode referenceNode = database.getReferenceNode();
referenceNodeId = referenceNode.getId();
} finally {
database.txFinish();
}
} catch (Exception e) {
Ln.e(e, "database exception");
showErrorDialog();
}
}
private void updateNodesList() {
try {
referenceNodeLayout.removeAllViews();
rootNodesLayout.removeAllViews();
cycleNodesLayout.removeAllViews();
GraphDatabase database = dbManager.getCurrentNeo4jDatabase();
database.beginTx();
try {
ParcelableNode referenceNode = database.getReferenceNode();
addNodeListItem(referenceNodeLayout, referenceNode);
List<ParcelableNode> rootNodes = new ArrayList<ParcelableNode>();
NodeIterator nodeIterator = database.getAllNodes();
while (nodeIterator.hasNext()) {
ParcelableNode node = nodeIterator.next();
if (!node.hasRelationship(Direction.INCOMING)) {
rootNodes.add(node);
}
}
for (ParcelableNode rootNode : rootNodes) {
addNodeListItem(rootNodesLayout, rootNode);
}
// TODO: [eRiC] what happens if all graphs are circular? ->
// pick
// one node per graph:
} finally {
database.txFinish();
}
} catch (Exception e) {
Ln.e(e, "database exception");
showErrorDialog();
}
}
private void addNodeListItem(LinearLayout nodeLayout, ParcelableNode node) {
View nodesListItem = inflater.inflate(R.layout.rootnode_activity_listitem, null);
Button nodeButton = (Button) nodesListItem.findViewById(R.id.rootNodeButton);
final long nodeId = node.getId();
nodeButton.setText("" + nodeId);
nodeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showNodeActivity(nodeId);
}
});
nodeLayout.addView(nodesListItem);
}
private void showErrorDialog() {
AlertDialog dialog = new AlertDialog.Builder(context).create();
dialog.setTitle(R.string.error_title);
dialog.setMessage(getResources().getText(R.string.database_error));
dialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();
}
private void showNodeActivity(long nodeId) {
Intent intent = new Intent(getApplicationContext(), NodeActivity.class);
intent.putExtra(DBInspectorConstants.INTENTEXTRA_CENTER_NODEID, nodeId);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
startActivity(intent);
}
private void askCreateNode() {
AlertDialog dialog = new AlertDialog.Builder(this).create();
dialog.setTitle(R.string.node_create_title);
View nodeCreateView = inflater.inflate(R.layout.node_create, null);
final CheckBox nodeCreateDefaultRelationShipCheckBox = (CheckBox) nodeCreateView
.findViewById(R.id.nodeCreateDefaultRelationShipCheckBox);
nodeCreateDefaultRelationShipCheckBox.setChecked(true);
dialog.setView(nodeCreateView);
dialog.setButton(getResources().getString(android.R.string.ok), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
boolean createDefaultRelationship = nodeCreateDefaultRelationShipCheckBox.isChecked();
try {
createNode(createDefaultRelationship);
} catch (Exception e) {
Ln.e(e, "could not create node");
showErrorDialog();
}
updateNodesList();
}
});
dialog.setButton2(getResources().getString(android.R.string.cancel), new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
dialog.show();
}
private void createNode(boolean createDefaultRelationship) throws RemoteException, Neo4jServiceException {
GraphDatabase database = dbManager.getCurrentNeo4jDatabase();
database.beginTx();
try {
ParcelableNode node = new ParcelableNode();
long nodeId = database.createNode(node);
Ln.i("created node '" + node + "' with id '" + nodeId + "'");
if (createDefaultRelationship) {
ParcelableRelationship relationship = new ParcelableRelationship();
relationship.setStartNodeId(referenceNodeId);
relationship.setEndNodeId(nodeId);
relationship.setName(defaultRelationshipName);
long relationshipId = database.createRelationship(relationship);
Ln.i("created relationship '" + relationship + "' with id '" + relationshipId + "'");
}
database.txSuccess();
} finally {
database.txFinish();
}
}
}