/*
This file belongs to the Servoy development and deployment environment, Copyright (C) 1997-2010 Servoy BV
This program 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.
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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License along
with this program; if not, see http://www.gnu.org/licenses or write to the Free
Software Foundation,Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301
*/
package com.servoy.j2db.debug;
import java.awt.EventQueue;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.swing.SwingUtilities;
import org.eclipse.dltk.rhino.dbgp.DBGPDebugger;
import org.mozilla.javascript.RhinoException;
import org.sablo.specification.PropertyDescription;
import org.sablo.specification.WebComponentSpecProvider;
import org.sablo.specification.WebObjectSpecification;
import com.servoy.j2db.ClientState;
import com.servoy.j2db.IFormController;
import com.servoy.j2db.component.ComponentFactory;
import com.servoy.j2db.dataprocessing.FoundSetManager;
import com.servoy.j2db.debug.DebugJ2DBClient.DebugSwingFormMananger;
import com.servoy.j2db.persistence.Field;
import com.servoy.j2db.persistence.Form;
import com.servoy.j2db.persistence.IPersist;
import com.servoy.j2db.persistence.IPersistVisitor;
import com.servoy.j2db.persistence.IRepository;
import com.servoy.j2db.persistence.IScriptProvider;
import com.servoy.j2db.persistence.Media;
import com.servoy.j2db.persistence.Relation;
import com.servoy.j2db.persistence.ScriptCalculation;
import com.servoy.j2db.persistence.ScriptMethod;
import com.servoy.j2db.persistence.ScriptVariable;
import com.servoy.j2db.persistence.Solution;
import com.servoy.j2db.persistence.Style;
import com.servoy.j2db.persistence.Tab;
import com.servoy.j2db.persistence.TableNode;
import com.servoy.j2db.persistence.ValueList;
import com.servoy.j2db.persistence.WebComponent;
import com.servoy.j2db.scripting.FormScope;
import com.servoy.j2db.scripting.IExecutingEnviroment;
import com.servoy.j2db.scripting.LazyCompilationScope;
import com.servoy.j2db.server.ngclient.property.types.RelationPropertyType;
import com.servoy.j2db.server.ngclient.property.types.ValueListPropertyType;
import com.servoy.j2db.util.Debug;
import com.servoy.j2db.util.PersistHelper;
import com.servoy.j2db.util.ServoyException;
import com.servoy.j2db.util.Utils;
public class DebugUtils
{
public interface DebugUpdateFormSupport
{
public void updateForm(Form form);
}
public static void errorToDebugger(IExecutingEnviroment engine, String message, Object errorDetail)
{
Object detail = errorDetail;
if (engine instanceof RemoteDebugScriptEngine)
{
DBGPDebugger debugger = ((RemoteDebugScriptEngine)engine).getDebugger();
if (debugger != null)
{
RhinoException rhinoException = null;
if (detail instanceof Exception)
{
Throwable exception = (Exception)detail;
while (exception != null)
{
if (exception instanceof RhinoException)
{
rhinoException = (RhinoException)exception;
break;
}
exception = exception.getCause();
}
}
String msg = message;
if (rhinoException != null)
{
if (msg == null)
{
msg = rhinoException.getLocalizedMessage();
}
else msg += '\n' + rhinoException.getLocalizedMessage();
msg += '\n' + rhinoException.getScriptStackTrace();
}
else if (detail instanceof Exception)
{
Object e = ((Exception)detail).getCause();
if (e != null)
{
detail = e;
}
msg += "\n > " + detail.toString(); // complete stack?
if (detail instanceof ServoyException && ((ServoyException)detail).getScriptStackTrace() != null)
{
msg += '\n' + ((ServoyException)detail).getScriptStackTrace();
}
}
else if (detail != null)
{
msg += "\n" + detail;
}
debugger.outputStdErr(msg.toString() + '\n');
}
}
}
public static void stdoutToDebugger(IExecutingEnviroment engine, Object message)
{
if (engine instanceof RemoteDebugScriptEngine)
{
DBGPDebugger debugger = ((RemoteDebugScriptEngine)engine).getDebugger();
if (debugger != null)
{
debugger.outputStdOut((message == null ? "<null>" : message.toString()) + '\n');
}
}
}
public static void infoToDebugger(IExecutingEnviroment engine, String message)
{
if (engine instanceof RemoteDebugScriptEngine)
{
DBGPDebugger debugger = ((RemoteDebugScriptEngine)engine).getDebugger();
if (debugger != null)
{
debugger.outputStdOut(message + '\n');
}
}
}
public static Set<IFormController>[] getScopesAndFormsToReload(final ClientState clientState, Collection<IPersist> changes)
{
Set<IFormController> scopesToReload = new HashSet<IFormController>();
final Set<IFormController> formsToReload = new HashSet<IFormController>();
Set<Form> formsUpdated = new HashSet<Form>();
for (IPersist persist : changes)
{
clientState.getFlattenedSolution().updatePersistInSolutionCopy(persist);
if (persist instanceof ScriptMethod)
{
if (persist.getParent() instanceof Form)
{
Form form = (Form)persist.getParent();
List<IFormController> cachedFormControllers = clientState.getFormManager().getCachedFormControllers(form);
for (IFormController formController : cachedFormControllers)
{
scopesToReload.add(formController);
}
}
else if (persist.getParent() instanceof Solution)
{
LazyCompilationScope scope = clientState.getScriptEngine().getScopesScope().getGlobalScope(((ScriptMethod)persist).getScopeName());
scope.remove((IScriptProvider)persist);
scope.put((IScriptProvider)persist, (IScriptProvider)persist);
}
else if (persist.getParent() instanceof TableNode)
{
clientState.getFoundSetManager().reloadFoundsetMethod(((TableNode)persist.getParent()).getDataSource(), (IScriptProvider)persist);
}
if (clientState instanceof DebugJ2DBClient)
{
// ((DebugJ2DBClient)clientState).clearUserWindows(); no need for this as window API was refactored and it allows users to clean up dialogs
((DebugSwingFormMananger)((DebugJ2DBClient)clientState).getFormManager()).fillScriptMenu();
}
}
else if (persist instanceof ScriptVariable)
{
ScriptVariable sv = (ScriptVariable)persist;
if (persist.getParent() instanceof Solution)
{
clientState.getScriptEngine().getScopesScope().getGlobalScope(sv.getScopeName()).put(sv);
}
if (persist.getParent() instanceof Form)
{
Form form = (Form)persist.getParent();
List<IFormController> cachedFormControllers = clientState.getFormManager().getCachedFormControllers(form);
for (IFormController formController : cachedFormControllers)
{
FormScope scope = formController.getFormScope();
scope.put(sv);
}
}
}
else if (persist.getAncestor(IRepository.FORMS) != null)
{
Form form = (Form)persist.getAncestor(IRepository.FORMS);
if (!formsUpdated.contains(form))
{
formsUpdated.add(form);
List<IFormController> cachedFormControllers = clientState.getFormManager().getCachedFormControllers(form);
for (IFormController formController : cachedFormControllers)
{
formsToReload.add(formController);
}
}
if (persist instanceof Form && clientState.getFormManager() instanceof DebugUtils.DebugUpdateFormSupport)
{
((DebugUtils.DebugUpdateFormSupport)clientState.getFormManager()).updateForm((Form)persist);
}
}
else if (persist instanceof ScriptCalculation)
{
ScriptCalculation sc = (ScriptCalculation)persist;
if (((RemoteDebugScriptEngine)clientState.getScriptEngine()).recompileScriptCalculation(sc))
{
List<String> al = new ArrayList<String>();
al.add(sc.getDataProviderID());
try
{
String dataSource = clientState.getFoundSetManager().getDataSource(sc.getTable());
((FoundSetManager)clientState.getFoundSetManager()).getRowManager(dataSource).clearCalcs(null, al);
((FoundSetManager)clientState.getFoundSetManager()).flushSQLSheet(dataSource);
}
catch (Exception e)
{
Debug.error(e);
}
}
// if (clientState instanceof DebugJ2DBClient)
// {
// ((DebugJ2DBClient)clientState).clearUserWindows(); no need for this as window API was refactored and it allows users to clean up dialogs
// }
}
else if (persist instanceof Relation)
{
((FoundSetManager)clientState.getFoundSetManager()).flushSQLSheet((Relation)persist);
List<IFormController> cachedFormControllers = clientState.getFormManager().getCachedFormControllers();
try
{
String primary = ((Relation)persist).getPrimaryDataSource();
for (IFormController formController : cachedFormControllers)
{
if (primary.equals(formController.getDataSource()))
{
final IFormController finalController = formController;
final Relation finalRelation = (Relation)persist;
formController.getForm().acceptVisitor(new IPersistVisitor()
{
@Override
public Object visit(IPersist o)
{
if (o instanceof Tab && Utils.equalObjects(finalRelation.getName(), ((Tab)o).getRelationName()))
{
formsToReload.add(finalController);
return o;
}
if (o instanceof Field && ((Field)o).getValuelistID() > 0)
{
ValueList vl = clientState.getFlattenedSolution().getValueList(((Field)o).getValuelistID());
if (vl != null && Utils.equalObjects(finalRelation.getName(), vl.getRelationName()))
{
formsToReload.add(finalController);
return o;
}
}
if (o instanceof WebComponent)
{
WebComponent webComponent = (WebComponent)o;
WebObjectSpecification spec = WebComponentSpecProvider.getInstance() != null
? WebComponentSpecProvider.getInstance().getWebComponentSpecification(webComponent.getTypeName()) : null;
if (spec != null)
{
Collection<PropertyDescription> properties = spec.getProperties(RelationPropertyType.INSTANCE);
for (PropertyDescription pd : properties)
{
if (Utils.equalObjects(webComponent.getFlattenedJson().opt(pd.getName()), finalRelation.getName()))
{
formsToReload.add(finalController);
return o;
}
}
}
}
return CONTINUE_TRAVERSAL;
}
});
}
}
}
catch (Exception e)
{
Debug.error(e);
}
}
else if (persist instanceof ValueList)
{
ComponentFactory.flushValueList(clientState, (ValueList)persist);
List<IFormController> cachedFormControllers = clientState.getFormManager().getCachedFormControllers();
for (IFormController formController : cachedFormControllers)
{
final IFormController finalController = formController;
final ValueList finalValuelist = (ValueList)persist;
formController.getForm().acceptVisitor(new IPersistVisitor()
{
@Override
public Object visit(IPersist o)
{
if (o instanceof Field && ((Field)o).getValuelistID() > 0 &&
Utils.equalObjects(((Field)o).getValuelistID(), finalValuelist.getID()))
{
formsToReload.add(finalController);
return o;
}
if (o instanceof WebComponent)
{
WebComponent webComponent = (WebComponent)o;
WebObjectSpecification spec = WebComponentSpecProvider.getInstance() != null
? WebComponentSpecProvider.getInstance().getWebComponentSpecification(webComponent.getTypeName()) : null;
if (spec != null)
{
Collection<PropertyDescription> properties = spec.getProperties(ValueListPropertyType.INSTANCE);
for (PropertyDescription pd : properties)
{
if (Utils.equalObjects(webComponent.getFlattenedJson().opt(pd.getName()), finalValuelist.getUUID().toString()))
{
formsToReload.add(finalController);
return o;
}
}
}
}
return CONTINUE_TRAVERSAL;
}
});
}
}
else if (persist instanceof Style)
{
ComponentFactory.flushStyle(null, ((Style)persist));
List<IFormController> cachedFormControllers = clientState.getFormManager().getCachedFormControllers();
String styleName = ((Style)persist).getName();
for (IFormController formController : cachedFormControllers)
{
if (styleName.equals(formController.getForm().getStyleName()))
{
formsToReload.add(formController);
}
}
}
else if (persist instanceof Media && PersistHelper.getOrderedStyleSheets(clientState.getFlattenedSolution()).contains(((Media)persist).getName()))
{
List<IFormController> cachedFormControllers = clientState.getFormManager().getCachedFormControllers();
for (IFormController formController : cachedFormControllers)
{
formsToReload.add(formController);
}
}
}
return new Set[] { scopesToReload, formsToReload };
}
/**
* This method must be invoked from the swt thread to deal with mac os 10.8 deadlock problems when the awt thread freezes with the stack :
* <p>
* <i>at apple.awt.CInputMethod.getNativeLocale(Native Method)
* at apple.awt.CToolkit.getDefaultKeyboardLocale(CToolkit.java:1044)</i>
* <p>
* //https://bugs.eclipse.org/bugs/show_bug.cgi?id=372951#c7
* apply workaround from https://bugs.eclipse.org/bugs/show_bug.cgi?id=291326 plus read and dispatch
* @param run : run must be <b>final</b>
* @throws InvocationTargetException
*/
public static void invokeAndWaitWhileDispatchingOnSWT(final Runnable run) throws InterruptedException, InvocationTargetException
{
// apply workaround from https://bugs.eclipse.org/bugs/show_bug.cgi?id=291326 plus read and dispatch
if (EventQueue.isDispatchThread())
{// called from AWT dispatch thread
run.run();
}
else if (org.eclipse.swt.widgets.Display.getCurrent() == null)
{// called from non SWT thread
SwingUtilities.invokeAndWait(run);
}
else
{
final AtomicBoolean awtFinished = new AtomicBoolean(false);
final org.eclipse.swt.widgets.Display display = org.eclipse.swt.widgets.Display.getCurrent();
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
// do some AWT stuff here
try
{
run.run();
}
finally
{
awtFinished.set(true);
display.asyncExec(new Runnable()
{
public void run()
{
// deliberately empty, this is only to wake up a
// potentially waiting SWT-thread below
}
});
}
}
});
while (!awtFinished.get())
{
if (!display.readAndDispatch()) display.sleep();
}
}
}
}