/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright 2014 Neil C Smith. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 3 only, as * published by the Free Software Foundation. * * This code 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 * version 3 for more details. * * You should have received a copy of the GNU General Public License version 3 * along with this work; if not, see http://www.gnu.org/licenses/ * * * Please visit http://neilcsmith.net if you need additional information or * have any questions. */ package net.neilcsmith.praxis.live.pxr; import java.util.Arrays; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.logging.Level; import java.util.logging.Logger; import net.neilcsmith.praxis.core.ArgumentFormatException; import net.neilcsmith.praxis.core.CallArguments; import net.neilcsmith.praxis.core.ComponentAddress; import net.neilcsmith.praxis.core.ComponentType; import net.neilcsmith.praxis.core.info.ComponentInfo; import net.neilcsmith.praxis.live.components.api.Components; import net.neilcsmith.praxis.live.core.api.Callback; import net.neilcsmith.praxis.live.model.Connection; import net.neilcsmith.praxis.live.model.ProxyException; import net.neilcsmith.praxis.live.project.api.PraxisProject; import net.neilcsmith.praxis.live.properties.PraxisProperty; import net.neilcsmith.praxis.live.pxr.PXRParser.AttributeElement; import net.neilcsmith.praxis.live.pxr.PXRParser.ComponentElement; import net.neilcsmith.praxis.live.pxr.PXRParser.ConnectionElement; import net.neilcsmith.praxis.live.pxr.PXRParser.Element; import net.neilcsmith.praxis.live.pxr.PXRParser.PropertyElement; import net.neilcsmith.praxis.live.pxr.PXRParser.RootElement; import org.openide.util.Exceptions; /** * * @author Neil C Smith (http://neilcsmith.net) */ class PXRBuilder { private final static Logger LOG = Logger.getLogger(PXRBuilder.class.getName()); private final PraxisProject project; private final PXRDataObject source; private final RootElement root; private final List<String> warnings; private Iterator<Element> iterator; private Callback processCallback; private PXRRootProxy rootProxy; private boolean processed; private boolean registerRoot; PXRBuilder(PraxisProject project, PXRDataObject source, RootElement root, List<String> warnings) { this.project = project; this.source = source; this.root = root; registerRoot = true; this.warnings = warnings; } PXRBuilder(PXRRootProxy rootProxy, RootElement root, List<String> warnings) { this.project = null; this.source = null; this.rootProxy = rootProxy; this.root = root; registerRoot = false; this.warnings = warnings; } void process(Callback callback) { if (callback == null) { throw new NullPointerException(); } this.processCallback = callback; if (Components.getRewriteDeprecated()) { ElementRewriter rewriter = new ElementRewriter(root, warnings); rewriter.process(); } buildElementIterator(); process(); } private void process() { while (iterator.hasNext()) { if (!process(iterator.next())) { //break; return; } } if (!processed && !iterator.hasNext()) { processed = true; if (registerRoot) { PXRRootRegistry.getDefault().register(rootProxy); } processCallback.onReturn(CallArguments.EMPTY); } } private boolean process(Element element) { if (element instanceof PropertyElement) { return processProperty((PropertyElement) element); } else if (element instanceof AttributeElement) { return processAttribute((AttributeElement) element); } else if (element instanceof ConnectionElement) { return processConnection((ConnectionElement) element); } else if (element instanceof RootElement) { return processRoot((RootElement) element); } else if (element instanceof ComponentElement) { return processComponent((ComponentElement) element); } processCallback.onError(CallArguments.EMPTY); return false; } private void processError(CallArguments args) { processed = true; processCallback.onError(args); } private void warn(String msg) { if (warnings == null) { return; } warnings.add(msg); } private boolean processProperty(final PropertyElement prop) { LOG.log(Level.FINE, "Processing Property Element : {0}", prop.property); final PXRComponentProxy cmp = findComponent(prop.component.address); if (cmp == null) { propertyError(prop, CallArguments.EMPTY); return true; } PraxisProperty<?> p = cmp.getProperty(prop.property); if (p instanceof BoundArgumentProperty) { try { ((BoundArgumentProperty) p).setValue(prop.args[0], new Callback() { @Override public void onReturn(CallArguments args) { if (cmp.isDynamic()) { try { cmp.call("info", CallArguments.EMPTY, new Callback() { @Override public void onReturn(CallArguments args) { try { cmp.refreshInfo(ComponentInfo.coerce(args.get(0))); } catch (ArgumentFormatException ex) { Exceptions.printStackTrace(ex); } process(); } @Override public void onError(CallArguments args) { process(); } }); return; } catch (ProxyException ex) { Exceptions.printStackTrace(ex); } } else { process(); } } @Override public void onError(CallArguments args) { propertyError(prop, args); process(); } }); return false; } catch (Exception ex) { LOG.warning("Couldn't set property " + prop.property); } } propertyError(prop, CallArguments.EMPTY); return true; } private void propertyError(PropertyElement prop, CallArguments args) { String err = "Couldn't set property " + prop.component.address + "." + prop.property; warn(err); } private boolean processAttribute(AttributeElement attr) { PXRComponentProxy cmp = findComponent(attr.component.address); if (cmp != null) { cmp.setAttr(attr.key, attr.value); } return true; } private boolean processConnection(final ConnectionElement con) { LOG.fine("Processing Connection Element : " + con.port1 + " -> " + con.port2); try { PXRComponentProxy parent = findComponent(con.container.address); if (parent instanceof PXRContainerProxy) { ((PXRContainerProxy) parent).connect( new Connection(con.component1, con.port1, con.component2, con.port2), new Callback() { @Override public void onReturn(CallArguments args) { process(); } @Override public void onError(CallArguments args) { connectionError(con, args); process(); } }); return false; } } catch (Exception ex) { Exceptions.printStackTrace(ex); } connectionError(con, CallArguments.EMPTY); return true; } private void connectionError(ConnectionElement connection, CallArguments args) { String p1 = connection.container.address + "/" + connection.component1 + "!" + connection.port1; String p2 = connection.container.address + "/" + connection.component2 + "!" + connection.port2; String err = "Couldn't create connection " + p1 + " -> " + p2; warn(err); } private boolean processRoot(RootElement root) { if (rootProxy != null) { LOG.log(Level.FINE, "Root already exists - ignoring Root Element : {0}, Type : {1}", new Object[]{root.address, root.type}); return true; } LOG.log(Level.FINE, "Processing Root Element : {0}, Type : {1}", new Object[]{root.address, root.type}); try { final ComponentAddress ad = root.address; final ComponentType type = root.type; PXRHelper.getDefault().createComponentAndGetInfo(ad, type, new Callback() { @Override public void onReturn(CallArguments args) { try { rootProxy = new PXRRootProxy( project, source, ad.getRootID(), type, ComponentInfo.coerce(args.get(0))); process(); } catch (Exception ex) { Exceptions.printStackTrace(ex); onError(args); } } @Override public void onError(CallArguments args) { processError(args); } }); } catch (Exception ex) { Exceptions.printStackTrace(ex); processError(CallArguments.EMPTY); } return false; } private boolean processComponent(final ComponentElement cmp) { LOG.log(Level.FINE, "Processing Component Element : {0}, Type : {1}", new Object[]{cmp.address, cmp.type}); try { ComponentAddress address = cmp.address; final PXRComponentProxy parent = findComponent(address.getParentAddress()); if (parent instanceof PXRContainerProxy) { String id = address.getComponentID(address.getDepth() - 1); ((PXRContainerProxy) parent).addChild(id, cmp.type, new Callback() { @Override public void onReturn(CallArguments args) { if (parent.isDynamic()) { try { parent.call("info", CallArguments.EMPTY, new Callback() { @Override public void onReturn(CallArguments args) { try { parent.refreshInfo(ComponentInfo.coerce(args.get(0))); } catch (ArgumentFormatException ex) { Exceptions.printStackTrace(ex); } process(); } @Override public void onError(CallArguments args) { process(); } }); return; } catch (ProxyException ex) { Exceptions.printStackTrace(ex); } } else { process(); } } @Override public void onError(CallArguments args) { componentError(cmp, args); process(); } }); return false; } } catch (Exception ex) { Exceptions.printStackTrace(ex); } componentError(cmp, CallArguments.EMPTY); return true; } private void componentError(ComponentElement cmp, CallArguments args) { String err = "Couldn't create component " + cmp.address; warn(err); } private PXRComponentProxy findComponent(ComponentAddress address) { if (rootProxy == null) { return null; } if (address.getDepth() == 1 && rootProxy.getAddress().equals(address)) { return rootProxy; } else if (!rootProxy.getAddress().getRootID().equals(address.getRootID())) { return null; } PXRComponentProxy cmp = rootProxy; for (int i = 1; i < address.getDepth(); i++) { if (cmp instanceof PXRContainerProxy) { cmp = ((PXRContainerProxy) cmp).getChild(address.getComponentID(i)); } else { return null; } } return cmp; } private synchronized void buildElementIterator() { if (iterator != null) { throw new IllegalStateException(); } List<Element> elements = new LinkedList<Element>(); addComponentElements(root, elements); iterator = elements.iterator(); } private void addComponentElements(ComponentElement component, List<Element> elements) { elements.add(component); elements.addAll(Arrays.asList(component.attributes)); elements.addAll(Arrays.asList(component.properties)); for (ComponentElement child : component.children) { addComponentElements(child, elements); } elements.addAll(Arrays.asList(component.connections)); } }