/*
Copyright 2008-2010 Gephi
Authors : Mathieu Bastian <mathieu.bastian@gephi.org>
Website : http://www.gephi.org
This file is part of Gephi.
Gephi is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
Gephi 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with Gephi. If not, see <http://www.gnu.org/licenses/>.
*/
package org.gephi.filters;
import java.beans.PropertyEditor;
import java.beans.PropertyEditorManager;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.gephi.filters.api.FilterController;
import org.gephi.filters.api.FilterLibrary;
import org.gephi.filters.api.FilterModel;
import org.gephi.filters.api.Query;
import org.gephi.filters.spi.Filter;
import org.gephi.filters.spi.FilterBuilder;
import org.gephi.filters.spi.FilterProperty;
import org.gephi.graph.api.GraphController;
import org.gephi.graph.api.GraphModel;
import org.gephi.graph.api.GraphView;
import org.gephi.project.api.Workspace;
import org.openide.util.Lookup;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
*
* @author Mathieu Bastian
*/
public class FilterModelImpl implements FilterModel {
private FilterLibraryImpl filterLibraryImpl;
private LinkedList<Query> queries;
private FilterThread filterThread;
private Query currentQuery;
private boolean filtering;
private GraphView currentResult;
private boolean autoRefresh;
private FilterAutoRefreshor autoRefreshor;
//Listeners
private List<ChangeListener> listeners;
public FilterModelImpl(Workspace workspace) {
filterLibraryImpl = new FilterLibraryImpl();
queries = new LinkedList<Query>();
listeners = new ArrayList<ChangeListener>();
autoRefresh = true;
GraphModel graphModel = Lookup.getDefault().lookup(GraphController.class).getModel(workspace);
autoRefreshor = new FilterAutoRefreshor(this, graphModel);
}
public FilterLibrary getLibrary() {
return filterLibraryImpl;
}
public Query[] getQueries() {
return queries.toArray(new Query[0]);
}
public boolean hasQuery(Query query) {
for (Query q : getQueries()) {
if (q == query) {
return true;
}
}
return false;
}
public void addFirst(Query function) {
queries.addFirst(function);
fireChangeEvent();
}
public void addLast(Query function) {
queries.addLast(function);
fireChangeEvent();
}
public void set(int index, Query function) {
queries.set(index, function);
}
public void remove(Query query) {
queries.remove(query);
destroyQuery(query);
fireChangeEvent();
}
public void rename(Query query, String name) {
((AbstractQueryImpl) query).setName(name);
fireChangeEvent();
}
public void setSubQuery(Query query, Query subQuery) {
//Clean
if (queries.contains(subQuery)) {
queries.remove(subQuery);
}
if (subQuery.getParent() != null) {
((AbstractQueryImpl) subQuery.getParent()).removeSubQuery(subQuery);
}
//Set
AbstractQueryImpl impl = (AbstractQueryImpl) query;
impl.addSubQuery(subQuery);
fireChangeEvent();
autoRefreshor.manualRefresh();
}
public void removeSubQuery(Query query, Query parent) {
AbstractQueryImpl impl = (AbstractQueryImpl) parent;
impl.removeSubQuery(query);
((AbstractQueryImpl) query).setParent(null);
fireChangeEvent();
autoRefreshor.manualRefresh();
}
public int getIndex(Query function) {
int i = 0;
for (Query f : queries) {
if (f == function) {
return i;
}
i++;
}
return -1;
}
public boolean isFiltering() {
return currentQuery != null && filtering;
}
public boolean isSelecting() {
return currentQuery != null && !filtering;
}
public void setFiltering(boolean filtering) {
this.filtering = filtering;
}
public void setSelecting(boolean selecting) {
this.filtering = !selecting;
}
public boolean isAutoRefresh() {
return autoRefresh;
}
public void setAutoRefresh(boolean autoRefresh) {
this.autoRefresh = autoRefresh;
fireChangeEvent();
if (!autoRefresh) {
autoRefreshor.setEnable(false);
} else if (autoRefresh && currentResult != null) {
autoRefreshor.setEnable(true);
}
}
public Query getCurrentQuery() {
return currentQuery;
}
public void setCurrentQuery(Query currentQuery) {
this.currentQuery = currentQuery;
fireChangeEvent();
}
public void updateParameters(Query query) {
if (query instanceof FilterQueryImpl) {
((FilterQueryImpl) query).updateParameters();
fireChangeEvent();
}
}
public Query getQuery(Filter filter) {
for (Query q : getAllQueries()) {
if (filter == q.getFilter()) {
return q;
}
}
return null;
}
public Query[] getAllQueries() {
List<Query> result = new ArrayList<Query>();
LinkedList<Query> stack = new LinkedList<Query>();
stack.addAll(queries);
while (!stack.isEmpty()) {
Query q = stack.pop();
result.add(q);
for (Query child : q.getChildren()) {
stack.add(child);
}
}
return result.toArray(new Query[0]);
}
public FilterThread getFilterThread() {
return filterThread;
}
public FilterAutoRefreshor getAutoRefreshor() {
return autoRefreshor;
}
public void setFilterThread(FilterThread filterThread) {
this.filterThread = filterThread;
}
public void setCurrentResult(GraphView currentResult) {
this.currentResult = currentResult;
if (currentResult != null && autoRefresh) {
autoRefreshor.setEnable(true);
} else if (currentResult == null && autoRefresh) {
autoRefreshor.setEnable(false);
}
}
public GraphView getCurrentResult() {
return currentResult;
}
public void destroy() {
if (filterThread != null) {
filterThread.setRunning(false);
}
autoRefreshor.setRunning(false);
currentResult = null;
listeners = null;
for (Query q : queries) {
destroyQuery(q);
}
}
private void destroyQuery(Query query) {
if (query instanceof AbstractQueryImpl) {
AbstractQueryImpl absQuery = (AbstractQueryImpl) query;
for (Query q : absQuery.getDescendantsAndSelf()) {
if (q instanceof FilterQueryImpl) {
Filter f = ((FilterQueryImpl) q).getFilter();
FilterBuilder builder = filterLibraryImpl.getBuilder(f);
if (builder != null) {
builder.destroy(f);
}
}
}
}
}
//EVENTS
public void addChangeListener(ChangeListener listener) {
if (!listeners.contains(listener)) {
listeners.add(listener);
}
}
public void removeChangeListener(ChangeListener listener) {
if (listeners != null) {
listeners.remove(listener);
}
}
private void fireChangeEvent() {
ChangeEvent evt = new ChangeEvent(this);
for (ChangeListener l : listeners) {
l.stateChanged(evt);
}
}
//PERSISTENCE
private int queryId = 0;
public Element writeXML(Document document) {
Element filterModelE = document.createElement("filtermodel");
filterModelE.setAttribute("autorefresh", String.valueOf(autoRefresh));
//Queries
Element queriesE = document.createElement("queries");
queryId = 0;
for (Query query : queries) {
writeQuery(document, queriesE, query, -1);
}
filterModelE.appendChild(queriesE);
return filterModelE;
}
private void writeQuery(Document document, Element parentElement, Query query, int parentId) {
Element queryE = document.createElement("query");
int id = queryId++;
queryE.setAttribute("id", String.valueOf(id));
if (parentId != -1) {
queryE.setAttribute("parent", String.valueOf(parentId));
}
//Params
for (int i = 0; i < query.getPropertiesCount(); i++) {
FilterProperty prop = query.getFilter().getProperties()[i];
Element paramE = writeParameter(document, i, prop);
if (paramE != null) {
queryE.appendChild(paramE);
}
}
//Filter
FilterBuilder builder = filterLibraryImpl.getBuilder(query.getFilter());
queryE.setAttribute("builder", builder.getClass().getName());
parentElement.appendChild(queryE);
for (Query child : query.getChildren()) {
writeQuery(document, parentElement, child, id);
}
}
private Element writeParameter(Document document, int index, FilterProperty property) {
Element parameterE = document.createElement("parameter");
parameterE.setAttribute("index", String.valueOf(index));
try {
PropertyEditor editor = property.getPropertyEditor();
if (editor == null) {
editor = PropertyEditorManager.findEditor(property.getValueType());
}
if (editor == null) {
return null;
}
editor.setValue(property.getValue());
parameterE.setTextContent(editor.getAsText());
return parameterE;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public void readXML(Element filterModelE) {
String autofresh = filterModelE.getAttribute("autorefresh");
if (autofresh != null && !autofresh.isEmpty()) {
autoRefresh = Boolean.parseBoolean(autofresh);
}
queries.clear();
Map<Integer, Query> idMap = new HashMap<Integer, Query>();
NodeList queryList = filterModelE.getElementsByTagName("query");
for (int i = 0; i < queryList.getLength(); i++) {
Node n = queryList.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Element queryE = (Element) n;
Query query = readQuery(queryE);
if (query != null) {
idMap.put(Integer.parseInt(queryE.getAttribute("id")), query);
if (queryE.hasAttribute("parent")) {
int parentId = Integer.parseInt(queryE.getAttribute("parent"));
Query parentQuery = idMap.get(parentId);
setSubQuery(parentQuery, query);
} else {
//Top query
addFirst(query);
}
}
}
}
}
private Query readQuery(Element queryE) {
String builderClassName = queryE.getAttribute("builder");
FilterBuilder builder = null;
for (FilterBuilder fb : filterLibraryImpl.getLookup().lookupAll(FilterBuilder.class)) {
if (fb.getClass().getName().equals(builderClassName)) {
builder = fb;
}
}
if (builder != null) {
//Create filter
Filter filter = builder.getFilter();
FilterController fc = Lookup.getDefault().lookup(FilterController.class);
Query query = fc.createQuery(filter);
//Params
NodeList paramList = queryE.getElementsByTagName("parameter");
for (int i = 0; i < paramList.getLength(); i++) {
Node n = paramList.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Element paramE = (Element) n;
int index = Integer.parseInt(paramE.getAttribute("index"));
FilterProperty property = query.getFilter().getProperties()[index];
try {
PropertyEditor editor = property.getPropertyEditor();
if (editor == null) {
editor = PropertyEditorManager.findEditor(property.getValueType());
}
if (editor != null) {
String textValue = paramE.getTextContent();
editor.setAsText(textValue);
property.setValue(editor.getValue());
updateParameters(query);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
return query;
}
return null;
}
}