text:
package es.bsc.compss.types.resources;

import es.bsc.compss.types.resources.exceptions.InvalidElementException;
import es.bsc.compss.types.resources.exceptions.ResourcesFileValidationException;
import es.bsc.compss.types.resources.jaxb.AdaptorType;
import es.bsc.compss.types.resources.jaxb.AdaptorsListType;
import es.bsc.compss.types.resources.jaxb.AttachedDiskType;
import es.bsc.compss.types.resources.jaxb.AttachedDisksListType;
import es.bsc.compss.types.resources.jaxb.BatchType;
import es.bsc.compss.types.resources.jaxb.CloudProviderType;
import es.bsc.compss.types.resources.jaxb.ComputeNodeType;
import es.bsc.compss.types.resources.jaxb.DataNodeType;
import es.bsc.compss.types.resources.jaxb.EndpointType;
import es.bsc.compss.types.resources.jaxb.ImageType;
import es.bsc.compss.types.resources.jaxb.ImagesType;
import es.bsc.compss.types.resources.jaxb.InstanceTypeType;
import es.bsc.compss.types.resources.jaxb.InstanceTypesType;
import es.bsc.compss.types.resources.jaxb.InteractiveType;
import es.bsc.compss.types.resources.jaxb.MemoryType;
import es.bsc.compss.types.resources.jaxb.OSType;
import es.bsc.compss.types.resources.jaxb.PriceType;
import es.bsc.compss.types.resources.jaxb.ProcessorType;
import es.bsc.compss.types.resources.jaxb.ResourcesExternalAdaptorProperties;
import es.bsc.compss.types.resources.jaxb.ResourcesNIOAdaptorProperties;
import es.bsc.compss.types.resources.jaxb.ServiceType;
import es.bsc.compss.types.resources.jaxb.SharedDiskType;
import es.bsc.compss.types.resources.jaxb.SoftwareListType;
import es.bsc.compss.types.resources.jaxb.StorageType;
import es.bsc.compss.types.resources.jaxb.SubmissionSystemType;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBElement;
import javax.xml.namespace.QName;

import org.apache.logging.log4j.Logger;

/**
 * Custom XML Validation for COMPSs Resources file.
 */
public class Validator {

    private ResourcesFile rf;

    private Logger logger;

    /**
     * Validator instantiation for ResourcesFile rf.
     *
     * @param rf Resource file to validate
     */
    public Validator(ResourcesFile rf, Logger logger) {
        this.logger = logger;
        this.rf = rf;
    }

    /**
     * Validates the content of the given ResourcesFile object. The content is correct if no exception is raised.
     *
     * @throws ResourcesFileValidationException Invalid data
     */
    public void validate() throws ResourcesFileValidationException {
        logger.info("Validating <ResourcesList> tag");
        // Check inner elements
        List<Object> objList = rf.getResources().getSharedDiskOrDataNodeOrComputeNode();
        if (objList != null) {
            try {
                for (Object obj : objList) {
                    if (obj instanceof SharedDiskType) {
                        validateSharedDisk(((SharedDiskType) obj));
                    } else if (obj instanceof DataNodeType) {
                        validateDataNode(((DataNodeType) obj));
                    } else if (obj instanceof ComputeNodeType) {
                        validateComputeNode(((ComputeNodeType) obj));
                    } else if (obj instanceof ServiceType) {
                        validateService(((ServiceType) obj));
                    } else if (obj instanceof CloudProviderType) {
                        validateCloudProvider(((CloudProviderType) obj));
                    } else {
                        throw new InvalidElementException("Resources", "Attribute" + obj.getClass(),
                            "Incorrect attribute");
                    }
                }
            } catch (InvalidElementException iee) {
                throw ((ResourcesFileValidationException) iee);
            }
        } else {
            // If resources is empty, raise exception
            throw new ResourcesFileValidationException("Empty resources file");
        }
        logger.info("End validation");
    }

    /* ******************* VALIDATION OF MAIN ELEMENTS *********************/
    /**
     * Validates the given SharedDiskType @sd with the current ResourcesFileType. The content is correct if no exception
     * is raised.
     *
     * @param sd Shared disk description
     * @throws InvalidElementException Error invalid data
     */
    public void validateSharedDisk(SharedDiskType sd) throws InvalidElementException {
        // Check that name isn't used
        int num = 0;
        for (String s : rf.getSharedDisks_names()) {
            if (s.equals(sd.getName())) {
                num = num + 1;
            }
        }
        if (num > 1) {
            // Shared disk already exists
            throw new InvalidElementException("SharedDisk", sd.getName(), "Name already in use");
        }

        // If storage exists (it is not mandatory), check if it is valid
        if (sd.getStorage() != null) {
            validateStorage(sd.getStorage());
        }
    }

    /**
     * Validates the given DataNodeType @dn with the current ResourcesFileType. The content is correct if no exception
     * is raised.
     *
     * @param dn Data node description
     * @throws InvalidElementException Error invalid data
     */
    public void validateDataNode(DataNodeType dn) throws InvalidElementException {
        // Check that name isn't used
        int num = 0;
        for (String d : rf.getDataNodes_names()) {
            if (d.equals(dn.getName())) {
                num = num + 1;
            }
        }
        if (num > 1) {
            // DataNode already exists
            throw new InvalidElementException("DataNode", dn.getName(), "Name already in use");
        }

        // Check inner elements
        List<JAXBElement<?>> innerElements = dn.getHostOrPathOrAdaptors();
        if (innerElements != null) {
            boolean hostTagFound = false;
            boolean pathTagFound = false;
            boolean adaptorsTagFound = false;
            boolean storageTagFound = false;
            boolean sharedDisksTagFound = false;
            for (JAXBElement<?> obj : innerElements) {
                if (obj.getName().equals(new QName("Host"))) {
                    if (hostTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("DataNode " + dn.getName(), "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        hostTagFound = true;
                    }
                } else if (obj.getName().equals(new QName("Path"))) {
                    if (pathTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("DataNode " + dn.getName(), "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        pathTagFound = true;
                    }
                } else if (obj.getName().equals(new QName("Adaptors"))) {
                    if (adaptorsTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("DataNode " + dn.getName(), "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        adaptorsTagFound = true;
                        validateAdaptors(((AdaptorsListType) obj.getValue()));
                    }
                } else if (obj.getName().equals(new QName("Storage"))) {
                    if (storageTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("DataNode " + dn.getName(), "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        storageTagFound = true;
                        validateStorage(((StorageType) obj.getValue()));
                    }
                } else if (obj.getName().equals(new QName("SharedDisks"))) {
                    if (sharedDisksTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("DataNode " + dn.getName(), "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        sharedDisksTagFound = true;
                        validateAttachedDisksList(((AttachedDisksListType) obj.getValue()));
                    }
                } else {
                    throw new InvalidElementException("DataNode " + dn.getName(), "Attribute " + obj.getName(),
                        "Incorrect attribute");
                }
            }

            // Check minimum appearences
            if (!hostTagFound) {
                throw new InvalidElementException("DataNode " + dn.getName(), "Attribute Host", "Doesn't appear");
            }
            if (!pathTagFound) {
                throw new InvalidElementException("DataNode " + dn.getName(), "Attribute Path", "Doesn't appear");
            }
            if (!adaptorsTagFound) {
                throw new InvalidElementException("DataNode " + dn.getName(), "Attribute Adaptors", "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("DataNode " + dn.getName(), "", "Content is empty");
        }
    }

    /**
     * Validates the given ComputeNodeType @cn with the current ResourcesFileType. The content is correct if no
     * exception is raised.
     *
     * @param cn Compute node description
     * @throws InvalidElementException Error invalid data
     */
    public void validateComputeNode(ComputeNodeType cn) throws InvalidElementException {
        // Check that name isn't used
        int num = 0;
        for (String c : rf.getComputeNodes_names()) {
            if (c.equals(cn.getName())) {
                num = num + 1;
            }
        }
        if (num > 1) {
            // ComputeNode already exists
            throw new InvalidElementException("ComputeNode", cn.getName(), "Name already in use");
        }

        // Check inner elements
        List<Object> innerElements = cn.getProcessorOrAdaptorsOrMemory();
        if (innerElements != null) {
            List<String> processorNames = new ArrayList<String>();
            boolean processorTagFound = false;
            boolean adaptorsTagFound = false;
            boolean memoryTagFound = false;
            boolean storageTagFound = false;
            boolean osTagFound = false;
            boolean softwareTagFound = false;
            boolean sharedDisksTagFound = false;
            boolean priceTagFound = false;
            for (Object obj : innerElements) {
                if (obj instanceof ProcessorType) {
                    ProcessorType p = (ProcessorType) obj;
                    if (processorNames.contains(p.getName())) {
                        throw new InvalidElementException("ComputeNode " + cn.getName(),
                            "Attribute Processor" + p.getName(), "Appears more than once");
                    } else {
                        processorTagFound = true;
                        processorNames.add(p.getName());
                        validateProcessor(p);
                    }
                } else if (obj instanceof AdaptorsListType) {
                    if (adaptorsTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                            "Appears more than once");
                    } else {
                        adaptorsTagFound = true;
                        validateAdaptors(((AdaptorsListType) obj));
                    }
                } else if (obj instanceof MemoryType) {
                    if (memoryTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                            "Appears more than once");
                    } else {
                        memoryTagFound = true;
                        validateMemory(((MemoryType) obj));
                    }
                } else if (obj instanceof StorageType) {
                    if (storageTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                            "Appears more than once");
                    } else {
                        storageTagFound = true;
                        validateStorage(((StorageType) obj));
                    }
                } else if (obj instanceof OSType) {
                    if (osTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                            "Appears more than once");
                    } else {
                        osTagFound = true;
                        validateOS(((OSType) obj));
                    }
                } else if (obj instanceof SoftwareListType) {
                    if (softwareTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                            "Appears more than once");
                    } else {
                        softwareTagFound = true;
                        validateSoftwareList(((SoftwareListType) obj));
                    }
                } else if (obj instanceof AttachedDisksListType) {
                    if (sharedDisksTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                            "Appears more than once");
                    } else {
                        sharedDisksTagFound = true;
                        validateAttachedDisksList(((AttachedDisksListType) obj));
                    }
                } else if (obj instanceof PriceType) {
                    if (priceTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                            "Appears more than once");
                    } else {
                        priceTagFound = true;
                        validatePrice(((PriceType) obj));
                    }
                } else {
                    throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute " + obj.getClass(),
                        "Incorrect attribute");
                }
            }
            // Check minimum appearences
            if (!processorTagFound) {
                throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute Processor",
                    "Doesn't appear");
            }
            if (!adaptorsTagFound) {
                throw new InvalidElementException("ComputeNode " + cn.getName(), "Attribute Adaptors",
                    "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("ComputeNode " + cn.getName(), "", "Content is empty");
        }
    }

    /**
     * Validates the given ServiceType @s with the current ResourcesFileType. The content is correct if no exception is
     * raised.
     *
     * @param s Service description
     * @throws InvalidElementException Error invalid data
     */
    public void validateService(ServiceType s) throws InvalidElementException {
        // Check that name isn't used
        int num = 0;
        for (String s2 : rf.getServices_wsdls()) {
            if (s2.equals(s.getWsdl())) {
                num = num + 1;
            }
        }
        if (num > 1) {
            // Service already exists
            throw new InvalidElementException("Service", s.getWsdl(), "WSDL already in use");
        }

        // Check that mandatory tags are present
        if ((s.getName() == null) || (s.getName().isEmpty())) {
            throw new InvalidElementException("Service " + s.getWsdl(), "Attribute Name", "Doesn't appear");
        }
        if ((s.getNamespace() == null) || (s.getNamespace().isEmpty())) {
            throw new InvalidElementException("Service " + s.getWsdl(), "Attribute Namespace", "Doesn't appear");
        }
        if ((s.getPort() == null) || (s.getPort().isEmpty())) {
            throw new InvalidElementException("Service " + s.getWsdl(), "Attribute Port", "Doesn't appear");
        }

        // Check inner elements
        if (s.getPrice() != null) {
            validatePrice(s.getPrice());
        }
    }

    /**
     * Validates the given CloudProviderType @cp with the current ResourcesFileType. The content is correct if no
     * exception is raised.
     *
     * @param cp Cloud provider description
     * @throws InvalidElementException Error invalid data
     */
    public void validateCloudProvider(CloudProviderType cp) throws InvalidElementException {
        // Check that name isn't used
        int num = 0;
        for (String cp2 : rf.getCloudProviders_names()) {
            if (cp2.equals(cp.getName())) {
                num = num + 1;
            }
        }
        if (num > 1) {
            // CloudProvider already exists
            throw new InvalidElementException("CloudProvider", cp.getName(), "Name already in use");
        }

        // Check inner elements
        if (cp.getEndpoint() == null) {
            throw new InvalidElementException("CloudProvider " + cp.getName(), "Attribute Endpoint", "Doesn't appear");
        } else {
            validateEndpoint(cp.getEndpoint());
        }
        if (cp.getImages() == null) {
            throw new InvalidElementException("CloudProvider " + cp.getName(), "Attribute Images", "Doesn't appear");
        } else {
            validateImages(cp.getImages());
        }
        if (cp.getInstanceTypes() == null) {
            throw new InvalidElementException("CloudProvider " + cp.getName(), "Attribute InstanceTypes",
                "Doesn't appear");
        } else {
            validateInstanceTypes(cp.getInstanceTypes());
        }
    }

    /* ********** HELPERS FOR VALIDATION (PRIVATE METHODS) *************/

    private void validateStorage(StorageType s) throws InvalidElementException {
        // Validate inner elements
        List<Serializable> innerElements = s.getSizeOrTypeOrBandwidth();
        if (innerElements != null) {
            boolean sizeTagFound = false;
            boolean typeTagFound = false;
            boolean bandwidthTagFound = false;
            for (Object obj : innerElements) {
                if (obj instanceof Float) {
                    if (sizeTagFound) {
                        throw new InvalidElementException("Storage", "Attribute Size", "Appears more than once");
                    } else {
                        sizeTagFound = true;
                        Float val = (Float) obj;
                        if (val <= 0.0) {
                            throw new InvalidElementException("Storage", "Attribute Size", "Must be greater than 0");
                        }
                    }
                } else if (obj instanceof String) {
                    if (typeTagFound) {
                        throw new InvalidElementException("Storage", "Attribute Type", "Appears more than once");
                    } else {
                        typeTagFound = true;
                    }
                } else if (obj instanceof Integer) {
                    if (bandwidthTagFound) {
                        throw new InvalidElementException("Storage", "Attribute Bandwidth", "Appears more than once");
                    } else {
                        bandwidthTagFound = true;
                        Integer val = (Integer) obj;
                        if (val < 0) {
                            throw new InvalidElementException("Storage", "Attribute Bandwidth",
                                "Must be greater than 0");
                        }
                    }
                } else {
                    throw new InvalidElementException("Storage", "Attribute " + obj.getClass(), "Incorrect attribute");
                }
            }
            // Check minimum appearences
            if (!sizeTagFound) {
                throw new InvalidElementException("Storage ", "Attribute Size", "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("Storage", "", "Content is empty");
        }
    }

    private void validateAdaptors(AdaptorsListType adaptors) throws InvalidElementException {
        // Validate inner elements
        if (adaptors != null) {
            List<AdaptorType> adaptorsList = adaptors.getAdaptor();
            if (adaptorsList != null) {
                for (AdaptorType adaptor : adaptorsList) {
                    validateAdaptor(adaptor);
                }
            } else {
                // Empty inner elements
                throw new InvalidElementException("Adaptors", "", "Content is empty");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("Adaptors", "", "Tag not present");
        }
    }

    private void validateAdaptor(AdaptorType adaptor) throws InvalidElementException {
        // Validate inner elements
        List<JAXBElement<?>> innerElements = adaptor.getSubmissionSystemOrPortsOrBrokerAdaptor();
        if (innerElements != null) {
            boolean subsysTagFound = false;
            boolean portsTagFound = false;
            boolean brokerAdaptorTagFound = false;
            boolean propertiesTagFound = false;
            boolean userTagFound = false;
            for (JAXBElement<?> obj : innerElements) {
                if (obj.getName().equals(new QName("SubmissionSystem"))) {
                    if (subsysTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("Adaptor", "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        subsysTagFound = true;
                        validateSubmissionSystem(((SubmissionSystemType) obj.getValue()));
                    }
                } else if (obj.getName().equals(new QName("Ports"))) {
                    if (portsTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("Adaptor", "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else if (brokerAdaptorTagFound) {
                        // Cannot define multiple adaptor properties
                        throw new InvalidElementException("Adaptor", "Attribute " + obj.getName(),
                            "An adaptor property (GAT) was already defined");
                    } else {
                        portsTagFound = true;
                        validateNIOAdaptorProperties(((ResourcesNIOAdaptorProperties) obj.getValue()));
                    }
                } else if (obj.getName().equals(new QName("BrokerAdaptor"))) {
                    if (brokerAdaptorTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("Adaptor", "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else if (portsTagFound) {
                        // Cannot define multiple adaptor properties
                        throw new InvalidElementException("Adaptor", "Attribute " + obj.getName(),
                            "An adaptor property (NIO) was already defined");
                    } else {
                        brokerAdaptorTagFound = true;
                        validateGATAdaptorProperties(((String) obj.getValue()));
                    }
                } else if (obj.getName().equals(new QName("Properties"))) {
                    if (propertiesTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("Adaptor", "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        propertiesTagFound = true;
                        validateExternalAdaptorProperties(((ResourcesExternalAdaptorProperties) obj.getValue()));
                    }
                } else if (obj.getName().equals(new QName("User"))) {
                    if (userTagFound) {
                        // Second occurency, throw exception
                        throw new InvalidElementException("Adaptor", "Attribute " + obj.getName(),
                            "Appears more than once");
                    } else {
                        userTagFound = true;
                    }
                } else {
                    throw new InvalidElementException("Adaptor " + adaptor.getName(), "Attribute" + obj.getName(),
                        "Incorrect attribute");
                }
            }
            // Check minimum appearences
            if (!subsysTagFound) {
                throw new InvalidElementException("Adaptor " + adaptor.getName(), "Attribute SubmissionSystem",
                    "Doesn't appear");
            }
            if (!portsTagFound && !brokerAdaptorTagFound && !propertiesTagFound) {
                throw new InvalidElementException("Adaptor " + adaptor.getName(),
                    "Attribute Adaptor properties (NIO, GAT or External)", "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("Adaptor", "", "Content is empty");
        }
    }

    private void validateSubmissionSystem(SubmissionSystemType subSys) throws InvalidElementException {
        List<Object> innerElements = subSys.getBatchOrInteractive();
        if (innerElements != null) {
            boolean batchTagFound = false;
            boolean interactiveTagFound = false;
            for (Object obj : innerElements) {
                if (obj instanceof BatchType) {
                    if (batchTagFound) {
                        throw new InvalidElementException("SubmissionSystem", "Attribute Batch",
                            "Appears more than once");
                    } else {
                        batchTagFound = true;
                        validateBatch(((BatchType) obj));
                    }
                } else if (obj instanceof InteractiveType) {
                    if (interactiveTagFound) {
                        throw new InvalidElementException("SubmissionSystem", "Attribute Interactive",
                            "Appears more than once");
                    } else {
                        interactiveTagFound = true;
                        validateInteractive(((InteractiveType) obj));
                    }
                } else {
                    throw new InvalidElementException("SubmissionSystem", "Attribute " + obj.getClass(),
                        "Invalid type");
                }
            }
            // Check minimum
            if (!batchTagFound && !interactiveTagFound) {
                throw new InvalidElementException("SubmissionSystem", "Interactive/Batch",
                    "At least 1 submission system must be defined");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("SubmissionSystem", "", "Content is empty");
        }
    }

    private void validateBatch(BatchType batch) throws InvalidElementException {
        // The queue information is only a string name selected by the user
        // and thus it is always valid. Nothing to check
    }

    private void validateInteractive(InteractiveType interactive) throws InvalidElementException {
        // The interactive type is empty and thus, always valid
    }

    private void validateNIOAdaptorProperties(ResourcesNIOAdaptorProperties props) throws InvalidElementException {
        if (props.getMaxPort() < 0) {
            throw new InvalidElementException("Properties of NIO Adaptor", "Attribute MaxPort", "Invalid value");
        }
        if (props.getMinPort() < 0) {
            throw new InvalidElementException("Properties of NIO Adaptor", "Attribute MinPort", "Invalid value");
        }
        if (props.getMaxPort() <= props.getMinPort()) {
            throw new InvalidElementException("Properties of NIO Adaptor", "Attribute MinPort-MaxPort",
                "Invalid range");
        }
    }

    private void validateGATAdaptorProperties(String broker) throws InvalidElementException {
        if ((broker == null) || (broker.isEmpty())) {
            throw new InvalidElementException("GATAdaptor", "Attribute BrokerAdaptor", "Doesn't appear");
        }
    }

    private void validateExternalAdaptorProperties(ResourcesExternalAdaptorProperties props)
        throws InvalidElementException {
        // The list of properties is a user defined List< (Name,Value) >
        // Since the list is not empty (XSD validation) and the user can add any property
        // we consider that it is always valid.
    }

    private void validateAttachedDisksList(AttachedDisksListType disks) throws InvalidElementException {
        // Validate inner elements
        List<AttachedDiskType> innerElements = disks.getAttachedDisk();
        if (innerElements != null) {
            for (AttachedDiskType disk : innerElements) {
                validateAttachedDisk(disk);
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("AttachedDisksList", "", "Content is empty");
        }
    }

    private void validateAttachedDisk(AttachedDiskType disk) throws InvalidElementException {
        if (disk.getName() == null || disk.getName().isEmpty()) {
            throw new InvalidElementException("AttachedDisk ", "Attribute Name", "Doesn't appear");
        }
        if (rf.getSharedDisk(disk.getName()) == null) {
            // The disk has not been defined
            throw new InvalidElementException("AttachedDisk" + disk.getName(), "", "Not defined as SharedDisk");
        }
        if (disk.getMountPoint() == null || disk.getMountPoint().isEmpty()) {
            throw new InvalidElementException("AttachedDisk" + disk.getName(), "Attribute MountPoint",
                "Doesn't appear");
        }
    }

    private void validateProcessor(ProcessorType processor) throws InvalidElementException {
        // Names are validated in the parent node

        // Validate inner elements
        List<JAXBElement<?>> innerElements = processor.getComputingUnitsOrArchitectureOrSpeed();
        if (innerElements != null) {
            boolean cuTagFound = false;
            boolean architectureTagFound = false;
            boolean speedTagFound = false;
            boolean typeTagFound = false;
            boolean memTagFound = false;
            boolean processorPropertyTagFound = false;
            for (JAXBElement<?> obj : innerElements) {
                if (obj.getName().equals(new QName("ComputingUnits"))) {
                    if (cuTagFound) {
                        throw new InvalidElementException("Processor", "Attribute ComputingUnits",
                            "Appears more than once");
                    } else {
                        cuTagFound = true;
                        int val = (Integer) obj.getValue();
                        if (val <= 0) {
                            throw new InvalidElementException("Processor", "Attribute ComputingUnits",
                                "Must be greater than 0");
                        }
                    }
                } else if (obj.getName().equals(new QName("Architecture"))) {
                    if (architectureTagFound) {
                        throw new InvalidElementException("Processor", "Attribute Architecture",
                            "Appears more than once");
                    } else {
                        architectureTagFound = true;
                    }
                } else if (obj.getName().equals(new QName("Speed"))) {
                    if (speedTagFound) {
                        throw new InvalidElementException("Processor", "Attribute Speed", "Appears more than once");
                    } else {
                        speedTagFound = true;
                        float val = (Float) obj.getValue();
                        if (val <= 0.0) {
                            throw new InvalidElementException("Processor", "Attribute Speed", "Must be greater than 0");
                        }
                    }
                } else if (obj.getName().equals(new QName("Type"))) {
                    if (typeTagFound) {
                        throw new InvalidElementException("Processor", "Attribute Type", "Appears more than once");
                    } else {
                        typeTagFound = true;
                    }
                } else if (obj.getName().equals(new QName("InternalMemorySize"))) {
                    if (memTagFound) {
                        throw new InvalidElementException("Processor", "Attribute InternalMemorySize",
                            "Appears more than once");
                    } else {
                        memTagFound = true;
                        float val = (Float) obj.getValue();
                        if (val <= 0.0) {
                            throw new InvalidElementException("Processor", "Attribute InternalMemorySize",
                                "Must be greater than 0");
                        }
                    }
                } else if (obj.getName().equals(new QName("ProcessorProperty"))) {
                    if (processorPropertyTagFound) {
                        throw new InvalidElementException("Processor", "Attribute ProcessorProperty",
                            "Appears more than once");
                    } else {
                        processorPropertyTagFound = true;
                    }
                } else {
                    throw new InvalidElementException("Processor", "Attribute " + obj.getClass(),
                        "Incorrect attribute");
                }
            }
            // Check minimum appearences
            if (!cuTagFound) {
                throw new InvalidElementException("Processor", "Attribute ComputingUnits", "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("Processor", "", "Content is empty");
        }
    }

    private void validateMemory(MemoryType memory) throws InvalidElementException {
        // Validate inner elements
        List<Serializable> innerElements = memory.getSizeOrType();
        if (innerElements != null) {
            boolean sizeTagFound = false;
            boolean typeTagFound = false;
            for (Object obj : innerElements) {
                if (obj instanceof Float) {
                    if (sizeTagFound) {
                        throw new InvalidElementException("Memory", "Attribute Size", "Appears more than once");
                    } else {
                        sizeTagFound = true;
                        Float val = (Float) obj;
                        if (val <= 0.0) {
                            throw new InvalidElementException("Memory", "Attribute Size", "Must be greater than 0");
                        }
                    }
                } else if (obj instanceof String) {
                    if (typeTagFound) {
                        throw new InvalidElementException("Memory", "Attribute Type", "Appears more than once");
                    } else {
                        typeTagFound = true;
                    }
                } else {
                    throw new InvalidElementException("Memory", "Attribute " + obj.getClass(), "Incorrect attribute");
                }
            }
            // Check minimum appearences
            if (!sizeTagFound) {
                throw new InvalidElementException("Memory ", "Attribute Size", "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("Memory", "", "Content is empty");
        }
    }

    private void validateOS(OSType os) throws InvalidElementException {
        // The XSD ensures the OS Type and there is nothing more to validate
    }

    private void validateSoftwareList(SoftwareListType software) throws InvalidElementException {
        // The Software is a List of application names. The XSD ensure they are not empty
        // The names are user defined so there is nothig more to validate
    }

    private void validatePrice(PriceType price) throws InvalidElementException {
        if (price.getTimeUnit() <= 0) {
            throw new InvalidElementException("Price", "Attribute TimeUnit", "Invalid value");
        }
        if (price.getPricePerUnit() < 0) {
            throw new InvalidElementException("Price", "Attribute PricePerUnit", "Invalid value");
        }
    }

    private void validateEndpoint(EndpointType endpoint) throws InvalidElementException {
        // The endpoint has a Server / Connector / Port non-empty due to XSD
        // All the fields are user defined Strings and thus, there is nothing to validate
    }

    private void validateImages(ImagesType images) throws InvalidElementException {
        List<String> imageNames = new ArrayList<String>();

        List<ImageType> imagesList = images.getImage();
        if (imagesList != null) {
            for (ImageType im : imagesList) {
                if (imageNames.contains(im.getName())) {
                    throw new InvalidElementException("Images", "Attribute Image " + im.getName(), "Name already used");
                } else {
                    imageNames.add(im.getName());
                    validateImage(im);
                }
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("Images", "", "Content is empty");
        }
    }

    private void validateImage(ImageType image) throws InvalidElementException {
        // Validate inner elements
        List<Object> innerElements = image.getAdaptorsOrOperatingSystemOrSoftware();
        if (innerElements != null) {
            boolean adaptorsTagFound = false;
            boolean osTagFound = false;
            boolean softwareTagFound = false;
            boolean sdTagFound = false;
            boolean priceTagFound = false;
            boolean creationTimeTagFound = false;
            for (Object obj : innerElements) {
                if (obj instanceof AdaptorsListType) {
                    if (adaptorsTagFound) {
                        throw new InvalidElementException("Image", "Attribute Adaptors", "Appears more than once");
                    } else {
                        adaptorsTagFound = true;
                        validateAdaptors(((AdaptorsListType) obj));
                    }
                } else if (obj instanceof OSType) {
                    if (osTagFound) {
                        throw new InvalidElementException("Image", "Attribute OperatingSystem",
                            "Appears more than once");
                    } else {
                        osTagFound = true;
                        validateOS(((OSType) obj));
                    }
                } else if (obj instanceof SoftwareListType) {
                    if (softwareTagFound) {
                        throw new InvalidElementException("Image", "Attribute Software", "Appears more than once");
                    } else {
                        softwareTagFound = true;
                        validateSoftwareList(((SoftwareListType) obj));
                    }
                } else if (obj instanceof AttachedDisksListType) {
                    if (sdTagFound) {
                        throw new InvalidElementException("Image", "Attribute AttachedDisks", "Appears more than once");
                    } else {
                        sdTagFound = true;
                        this.validateAttachedDisksList(((AttachedDisksListType) obj));
                    }
                } else if (obj instanceof PriceType) {
                    if (priceTagFound) {
                        throw new InvalidElementException("Image", "Attribute Price", "Appears more than once");
                    } else {
                        priceTagFound = true;
                        validatePrice(((PriceType) obj));
                    }
                } else if (obj instanceof java.lang.Integer) { // Creation time (int)
                    if (creationTimeTagFound) {
                        throw new InvalidElementException("Image", "Attribute CreationTime", "Appears more than once");
                    } else {
                        creationTimeTagFound = true;
                        // Nothing to validate since it is an integer
                    }
                } else {
                    throw new InvalidElementException("Image", "Attribute " + obj.getClass(), "Incorrect attribute");
                }
            }
            // Check minimum appearences
            if (!adaptorsTagFound) {
                throw new InvalidElementException("Image ", "Attribute Adaptors", "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("Image", "", "Content is empty");
        }
    }

    private void validateInstanceTypes(InstanceTypesType instances) throws InvalidElementException {
        List<String> instanceNames = new ArrayList<String>();

        List<InstanceTypeType> instancesList = instances.getInstanceType();
        if (instancesList != null) {
            for (InstanceTypeType i : instances.getInstanceType()) {
                if (instanceNames.contains(i.getName())) {
                    throw new InvalidElementException("Instances", "Attribute Instance " + i.getName(),
                        "Name already used");
                } else {
                    instanceNames.add(i.getName());
                    validateInstance(i);
                }
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("InstanceTypes", "", "Content is empty");
        }
    }

    private void validateInstance(InstanceTypeType instance) throws InvalidElementException {
        // Validate inner elements
        List<Object> innerElements = instance.getProcessorOrMemoryOrStorage();
        if (innerElements != null) {
            List<String> processorNames = new ArrayList<String>();
            boolean processorTagFound = false;
            boolean memoryTagFound = false;
            boolean storageTagFound = false;
            boolean priceTagFound = false;
            for (Object obj : innerElements) {
                if (obj instanceof ProcessorType) {
                    processorTagFound = true;
                    ProcessorType p = (ProcessorType) obj;
                    if (processorNames.contains(p.getName())) {
                        throw new InvalidElementException("Instance", "Attribute Processor " + p.getName(),
                            "Appears more than once");
                    } else {
                        processorNames.add(p.getName());
                        validateProcessor(((ProcessorType) obj));
                    }
                } else if (obj instanceof MemoryType) {
                    if (memoryTagFound) {
                        throw new InvalidElementException("Instance", "Attribute Memory", "Appears more than once");
                    } else {
                        memoryTagFound = true;
                        validateMemory(((MemoryType) obj));
                    }
                } else if (obj instanceof StorageType) {
                    if (storageTagFound) {
                        throw new InvalidElementException("Instance", "Attribute Storage", "Appears more than once");
                    } else {
                        storageTagFound = true;
                        validateStorage(((StorageType) obj));
                    }
                } else if (obj instanceof PriceType) {
                    if (priceTagFound) {
                        throw new InvalidElementException("Instance", "Attribute Price", "Appears more than once");
                    } else {
                        priceTagFound = true;
                        validatePrice(((PriceType) obj));
                    }
                } else {
                    throw new InvalidElementException("Instance", "Attribute " + obj.getClass(), "Incorrect attribute");
                }
            }
            // Check minimum appearences
            if (!processorTagFound) {
                throw new InvalidElementException("Insntace", "Attribute Processor", "Doesn't appear");
            }
        } else {
            // Empty inner elements
            throw new InvalidElementException("InstanceType", "", "Content is empty");
        }
    }

}


text:
package org.eclipse.ceylon.compiler.java.test.issues;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

import org.eclipse.ceylon.compiler.java.test.CompilerTests;
import org.junit.Test;

/**
 * Special issue with source path mimicking a one-package qualified name
 * @author Stéphane Épardaud <stef@epardaud.fr>
 */
public class PackageIssuesTests extends CompilerTests {

    private String testDir;

    @Override
    protected String getSourcePath() {
        return super.getPackagePath() + testDir + File.separator;
    }

    @Override
    protected String getPackagePath() {
        return super.getPackagePath() + testDir + File.separator;
    }

    @Override
    protected String transformDestDir(String name) {
        return name + "-package-issues";
    }

    @Test
    public void testBug148(){
        testDir = "bug01xx";
        compareWithJavaSource("bug148/Bug148.src", "bug148/Bug148.ceylon");
        compareWithJavaSource("bug148_2/Bug148_2.src", "bug148_2/Bug148_2.ceylon");
        compareWithJavaSource("bug148_3/Bug148_3.src", "bug148_3/Bug148_3.ceylon");
    }

    @Test
    public void testBug187(){
        testDir = "bug01xx";
        compile("bug187/Main.ceylon");
        compareWithJavaSource("bug187/Bug187");
    }

    @Test
    public void testBug214(){
        testDir = "bug02xx";
        compareWithJavaSource("Bug214.src", "Bug214.ceylon");
    }

    @Test
    public void testBug542(){
        testDir = "bug05xx";
        compareWithJavaSource("Bug542");
    }

}

redpajama_set_name: RedPajamaGithub


text:
/* We shouldn't crash trying to produce the inlined structure type die debug info.  */
/* { dg-do compile } */
/* { dg-options "-O1 -g" } */
inline void bar(char a[], unsigned int l)
{
  asm volatile ("" :: "m" ( *(struct {char x[l]; } *)a));
}

void foo(void)
{
  bar (0, 0);
}


text:
using System;
using System.Collections.Generic;

using System.Text;
using System.Diagnostics;
using StateMasher;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Xna.Framework.Net;
using Microsoft.Xna.Framework.Storage;
using Microsoft.Xna.Framework.Design;
using Infiniminer;

namespace InterfaceItems
{
    class InterfaceTextInput : InterfaceElement
    {
        public string value = "";
        private bool partialInFocus = false;
        private bool inFocus=false;
        //Infiniminer.KeyMap keyMap;

        public InterfaceTextInput()
        {
            //keyMap = new Infiniminer.KeyMap();
        }

        public InterfaceTextInput(Infiniminer.InfiniminerGame gameInstance)
        {
            uiFont = gameInstance.Content.Load<SpriteFont>("font_04b08");
            //keyMap = new Infiniminer.KeyMap();
        }

        public InterfaceTextInput(Infiniminer.InfiniminerGame gameInstance, Infiniminer.PropertyBag pb)
        {
            uiFont = gameInstance.Content.Load<SpriteFont>("font_04b08");
            _P = pb;
            //keyMap = new Infiniminer.KeyMap();
        }

        public override void OnMouseDown(MouseButton button, int x, int y)
        {
            if (enabled && size.Contains(x, y))
                partialInFocus = true;
            else
                inFocus = false;
        }

        public override void OnMouseUp(MouseButton button, int x, int y)
        {
            if (enabled && partialInFocus && size.Contains(x, y))
            {
                inFocus = true;
                _P.PlaySound(Infiniminer.InfiniminerSound.ClickLow);
            }
            partialInFocus = false;
        }

        public override void OnCharEntered(EventInput.CharacterEventArgs e)
        {
            base.OnCharEntered(e);
            if ((int)e.Character < 32 || (int)e.Character > 126) //From space to tilde
                return; //Do nothing

            if (inFocus)
            {
                value += e.Character;
            }
        }

        public override void OnKeyDown(Keys key)
        {
            base.OnKeyDown(key);
            if (inFocus)
            {
                if (key == Keys.Enter)
                {
                    inFocus = false;
                    _P.PlaySound(Infiniminer.InfiniminerSound.ClickHigh);
                }
                else if (key == Keys.Back&&value.Length>0)
                    value = value.Substring(0, value.Length - 1);
                /*else if (keyMap.IsKeyMapped(key))
                {
                    value += keyMap.TranslateKey(key, Keyboard.GetState().IsKeyDown(Keys.LeftShift) || Keyboard.GetState().IsKeyDown(Keys.RightShift));
                }*/
            }
        }

        public override void Render(GraphicsDevice graphicsDevice)
        {
            if (visible && size.Width > 0 && size.Height > 0)
            {
                Color drawColour = new Color(1f, 1f, 1f);

                if (!enabled)
                    drawColour = new Color(.7f, .7f, .7f);
                else if (!inFocus)
                    drawColour = new Color(.85f, .85f, .85f);

                //Generate 1px white texture
                Texture2D shade = new Texture2D(graphicsDevice, 1, 1, 1, TextureUsage.None, SurfaceFormat.Color);
                shade.SetData(new Color[] { Color.White });

                //Draw base background
                SpriteBatch spriteBatch = new SpriteBatch(graphicsDevice);
                spriteBatch.Begin();
                spriteBatch.Draw(shade, size, drawColour);

                spriteBatch.DrawString(uiFont, value, new Vector2(size.X + size.Width / 2 - uiFont.MeasureString(value).X / 2, size.Y + size.Height / 2 - 8), Color.Black);

                if (text != "")
                {
                    //Draw text
                    spriteBatch.DrawString(uiFont, text, new Vector2(size.X, size.Y - 20), enabled ? Color.White : new Color(.7f, .7f, .7f));//drawColour);
                }

                /*spriteBatch.Begin(SpriteBlendMode.AlphaBlend, SpriteSortMode.Deferred, SaveStateMode.SaveState);
                spriteBatch.Draw(shade, new Rectangle(size.X, size.Y, size.Height, size.Height), drawColour);
                spriteBatch.Draw(shade, new Rectangle(size.X + size.Width - size.Height, size.Y, size.Height, size.Height), drawColour);

                //Draw line
                float sliderPercent = getPercent();
                int sliderPartialWidth = size.Height / 4;
                int midHeight = (int)(size.Height / 2) - 1;
                int actualWidth = size.Width - 2 * size.Height;
                int actualPosition = (int)(sliderPercent * actualWidth);
                spriteBatch.Draw(shade, new Rectangle(size.X, size.Y + midHeight, size.Width, 1), drawColour);

                //Draw slider
                spriteBatch.Draw(shade, new Rectangle(size.X + size.Height + actualPosition - sliderPartialWidth, size.Y + midHeight - sliderPartialWidth, size.Height / 2, size.Height / 2), drawColour);

                //Draw amount
                spriteBatch.DrawString(uiFont, (((float)(int)(value * 10)) / 10).ToString(), new Vector2(size.X, size.Y - 20), drawColour);
                */

                spriteBatch.End();
                shade.Dispose();
            }
        }
    }
}


text:
var _        = require('underscore'),
    Backbone = require('backbone');

/**
 * object that gets populated with values
 * from a 'login' user form
 **/
var _UserLoginForm = Backbone.Model.extend({
    defaults: {
        username : null,
        password : null
    },

    validation: {
        username : {
            required: true,
            msg: 'Please enter a Username'
        },
        password: {
            required: true,
            msg: "Please enter a password"
        }
    }
});
exports.UserLoginForm = _UserLoginForm;

function _emptyUserLoginForm() {
    return new _UserLoginForm();
}
exports.emptyUserLoginForm = _emptyUserLoginForm;


text:
module Rucksack
  module Packer
    class SimpleCssPacker < Rucksack::Packer::Base

      supports :stylesheets

      def pack(source, target)
        File.open(source, "r") do |source_file|

          css = source_file.read

          ##
          # Compression code borrowed from asset_packager
          # http://github.com/sbecker/asset_packager/tree/master
          css.gsub!(/\s+/, " ") # collapse space
          css.gsub!(/\/\*(.*?)\*\/ /, "") # remove comments - caution, might want to remove this if using css hacks
          css.gsub!(/\} /, "}\n") # add line breaks
          css.gsub!(/\n$/, "") # remove last break
          css.gsub!(/ \{ /, " {") # trim inside brackets
          css.gsub!(/; \}/, "}") # trim inside brackets

          File.open(target, "w+"){ |target_file| target_file << css }
        end
      end

    end
  end
end


text:
package com.example.just.asynctaskapplication.ui;

public interface IProgressCallback<Progress> {

    void onProgressUpdate(Progress progress);

}


text:
The other day we were talking about git workflows and noted that the
notation used in [the NVIE article](http://nvie.com/git-model "Git
Model") was pretty cool, but none of our git tools drew the commit
graph in that manner.

Wouldn't it be nice to have something like that.

# Whyday 2010

So, for my whyday project I decided to play around with the idea of
drawing swimlane diagrams for git repos.  There are two pieces to the
puzzle:

* Figuring out which commits go into which swimlanes, and
* Rendering the swimlane diagram

For Whyday I concentrated on the second part of the project,
generating the diagram.  I decided to use the HTML5 canvas object for
drawing, and a javascript library to structure the data.  Swimlanes is
the result of my Whyday efforts.

(see [Whyday](http://whyday.org "Whyday") for more details on Whyday)

# Example:

![sample swimlane](http://github.com/jimweirich/swimlanes/raw/master/samples/swimlanes.jpg)

# Code

The above graph was generated from the following data:

<pre>function drawGrid() {
  var sl = new SwimLanes();

  sl.drawOn("canvas", 1000, 800);

  sl.addBranch("Prod", 0);
  sl.addCommit('a127846d', 0, 'Initial commit (Jim Weirich)');
  sl.addBranch("Development", 1);
  sl.addCommit('b17c3898', 1, 'Development Branch (Jim Weirich)');
  sl.addBranch("Feature 1", 2);
  sl.addCommit('c1f8c863', 2, 'Feature branch (Jim Weirich)');
  sl.addCommit('c2641c99', 2, 'Another feature branch (Jim Weirich)');
  sl.addBranch("Feature 2", 3);
  sl.addCommit('d146ab65', 3, 'Starting another feature (Jim Weirich)');
  sl.addCommit('b220bb37', 1, 'Merge to dev (Jim Weirich)', 'm');
  sl.addCommit('a27bf806', 0, 'Merge to production (Jim Weirich)', 'm');
  sl.addBranch("Feature 3", 4);
  sl.addCommit('f12f1158', 4, 'huh (Jim Weirich)');
  sl.addCommit('d265be23', 3, 'fixed bugs (Jim Weirich)');
  sl.addCommit('f2e605dd', 4, 'bleh (Jim Weirich)');
  sl.addCommit('b3f70bb', 1, 'Merge feature branch 2 (Jim Weirich)', 'm');
  sl.addCommit('f346ab65', 4, 'meh (Jim Weirich)');
  sl.addCommit('f497f0a5', 4, 'oops (Jim Weirich)');
  sl.addCommit('b4f0bb37', 1, 'Merge feature branch 3 (Jim Weirich)', 'm');
  sl.addCommit('a3c2b1b7', 0, 'Merge to production (Jim Weirich)', 'm');

  sl.connect('a127846d', 'a27bf806');
  sl.connect('a27bf806', 'a3c2b1b7');
  sl.connect('a3c2b1b7', 'a444807b');

  sl.connect('b17c3898', 'b4f0bb37');

  sl.connect('c1f8c863', 'c2641c99');

  sl.connect('d146ab65', 'd265be23');

  sl.connect('f12f1158', 'f2e605dd');
  sl.connect('f2e605dd', 'f346ab65');
  sl.connect('f346ab65', 'f497f0a5');

  sl.connect('a127846d', 'b17c3898');
  sl.connect('a127846d', 'c1f8c863');
  sl.connect('a127846d', 'd146ab65');
  sl.connect('a27bf806', 'f12f1158');
  sl.connect('c2641c99', 'b220bb37');
  sl.connect('b220bb37', 'a27bf806');
  sl.connect('b4f0bb37', 'a3c2b1b7');
  sl.connect('f497f0a5', 'b4f0bb37');
  sl.connect('d265be23', 'b3f70bb');

  sl.render();
}
</pre>

# TODO

* Write some Ruby code to actually get a real git repository in that format.


text:
/**
 * Module dependencies.
 */

var Visitor = require('./')
  , nodes = require('../nodes')
  , Stack = require('../stack')
  , Frame = require('../stack/frame')
  , Scope = require('../stack/scope')
  , utils = require('../utils')
  , bifs = require('../functions')
  , dirname = require('path').dirname
  , colors = require('../colors')
  , fs = require('fs');

/**
 * Initialize a new `Evaluator` with the given `root` Node
 * and the following `options`.
 *
 * Options:
 *
 *   - `compress`  Compress the css output, defaults to false
 *   - `warn`  Warn the user of duplicate function definitions etc
 *
 * @param {Node} root
 * @api private
 */

var Evaluator = module.exports = function Evaluator(root, options) {
  options = options || {};
  Visitor.call(this, root);
  this.stack = new Stack;
  this.imports = options.imports || [];
  this.functions = options.functions || {};
  this.paths = options.paths || [];
  this.filename = options.filename;
  this.paths.push(dirname(options.filename || '.'));
  this.stack.push(this.global = new Frame(root));
  this.warnings = options.warn;
  this.options = options;
  this.calling = []; // TODO: remove, use stack
};

/**
 * Inherit from `Visitor.prototype`.
 */

Evaluator.prototype.__proto__ = Visitor.prototype;

/**
 * Proxy visit to expose node line numbers.
 *
 * @param {Node} node
 * @return {Node}
 * @api private
 */

var visit = Visitor.prototype.visit;
Evaluator.prototype.visit = function(node){
  try {
    return visit.call(this, node);
  } catch (err) {
    // TODO: less-lame hack to reference
    // the origin node source input
    this.lineno = this.lineno || node.lineno;
    err.str = err.str || node.source;
    err.stylusStack = err.stylusStack || this.stack.toString();
    throw err;
  }
};

/**
 * Perform evaluation setup:
 *
 *   - populate global scope
 *   - iterate imports
 *
 * @api private
 */

Evaluator.prototype.setup = function(){
  this.populateGlobalScope();
  this.imports.forEach(function(file){
    var expr = new nodes.Expression;
    expr.push(new nodes.String(file));
    this.visit(new nodes.Import(expr));
  }, this);
};

/**
 * Populate the global scope with:
 *
 *   - css colors
 *
 * @api private
 */

Evaluator.prototype.populateGlobalScope = function(){
  var scope = this.global.scope;
  Object.keys(colors).forEach(function(name){
    var rgb = colors[name]
      , rgba = new nodes.RGBA(rgb[0], rgb[1], rgb[2], 1)
      , node = new nodes.Ident(name, rgba);
    scope.add(node);
  });
};

/**
 * Evaluate the tree.
 *
 * @return {Node}
 * @api private
 */

Evaluator.prototype.evaluate = function(){
  this.setup();
  return this.visit(this.root);
};

/**
 * Visit Group.
 */

Evaluator.prototype.visitGroup = function(group){
  group.block = this.visit(group.block);
  return group;
};

/**
 * Visit Charset.
 */

Evaluator.prototype.visitCharset = function(charset){
  return charset;
};

/**
 * Visit Return.
 */

Evaluator.prototype.visitReturn = function(ret){
  ret.expr = this.visit(ret.expr);
  throw ret;
};

/**
 * Visit Media.
 */

Evaluator.prototype.visitMedia = function(media){
  media.block = this.visit(media.block);
  return media;
};

/**
 * Visit Keyframes.
 */

Evaluator.prototype.visitKeyframes = function(keyframes){
  keyframes.name = this.visit(keyframes.name).first.name;
  return keyframes;
};

/**
 * Visit Function.
 */

Evaluator.prototype.visitFunction = function(fn){
  // check local
  var local = this.stack.currentFrame.scope.lookup(fn.name);
  if (local) this.warn('local ' + local.nodeName + ' "' + fn.name + '" previously defined in this scope');

  // user-defined
  var user = this.functions[fn.name];
  if (user) this.warn('user-defined function "' + fn.name + '" is already defined');

  // BIF
  var bif = bifs[fn.name];
  if (bif) this.warn('built-in function "' + fn.name + '" is already defined');

  return fn;
};

/**
 * Visit Each.
 */

Evaluator.prototype.visitEach = function(each){
  var expr = utils.unwrap(this.visit(utils.unwrap(each.expr)))
    , len = expr.nodes.length
    , val = new nodes.Ident(each.val)
    , key = new nodes.Ident(each.key || '__index__')
    , scope = this.currentScope
    , block = this.currentBlock
    , vals = []
    , body;

  each.block.scope = false;
  for (var i = 0; i < len; ++i) {
    val.val = expr.nodes[i];
    key.val = new nodes.Unit(i);
    scope.add(val);
    scope.add(key);
    body = this.visit(each.block.clone());
    vals = vals.concat(body.nodes);
  }

  this.mixin(vals, block);
  return vals[vals.length - 1] || nodes.null;
};

/**
 * Visit Call.
 */

Evaluator.prototype.visitCall = function(call){
  var fn = this.lookup(call.name)
    , ret;

  // Variable function
  if (fn && 'expression' == fn.nodeName) {
    fn = fn.nodes[0];
  }

  // Not a function? try user-defined or built-ins
  if (fn && 'function' != fn.nodeName) {
    fn = this.lookupFunction(call.name);
  }

  // Undefined function, render literal css
  if (!fn || fn.nodeName != 'function') return this.literalCall(call);
  this.calling.push(call.name);

  // Massive stack
  if (this.calling.length > 200) {
    throw new RangeError('Maximum call stack size exceeded');
  }

  // First node in expression
  if (fn instanceof nodes.Expression) fn = fn.first;

  // Evaluate arguments
  var _ = this.return;
  this.return = true;
  var args = this.visit(call.args);
  this.return = _;

  // Built-in
  if (fn.fn) {
    ret = this.invokeBuiltin(fn.fn, args);
  // User-defined
  } else if (fn instanceof nodes.Function) {
    ret = this.invokeFunction(fn, args);
  }

  this.calling.pop();
  return ret;
};

/**
 * Visit Ident.
 */

Evaluator.prototype.visitIdent = function(ident){
  // Lookup
  if (nodes.null == ident.val) {
    var val = this.lookup(ident.name);
    return val ? this.visit(val) : ident;
  // Assign
  } else {
    var _ = this.return;
    this.return = true;
    ident.val = this.visit(ident.val);
    this.return = _;
    this.currentScope.add(ident);
    return ident.val;
  }
};

/**
 * Visit BinOp.
 */

Evaluator.prototype.visitBinOp = function(binop){
  // Special-case "is defined" pseudo binop
  if ('is defined' == binop.op) return this.isDefined(binop.left);

  var _ = this.return;
  this.return = true;
  // Visit operands
  var op = binop.op
    , ident = 'ident' == binop.left.nodeName
    , left = this.visit(binop.left)
    , right = this.visit(binop.right);
  this.return = _;

  // First node in expression
  if (!~['[]', 'in'].indexOf(op)) {
    left = left.first;
    right = right.first;
  }

  // Coercion
  switch (op) {
    case '[]':
    case 'in':
    case '||':
    case '&&':
    case 'is a':
      break;
    default:
      // Special-case '-' against ident
      if ('-' == op
        && 'ident' == left.nodeName
        && 'unit' == right.nodeName) {
        var expr = new nodes.Expression;
        right.val = -right.val;
        expr.push(left);
        expr.push(right);
        return expr;
      }

      // Attempt coercion
      try {
        right = left.coerce(right);
      } catch (err) {
        // Disgregard coercion issues
        // and simply return false
        if ('==' == op || '!=' == op) {
          return nodes.false;
        } else {
          throw err;
        }
      }
  }

  // Operate
  return this.visit(left.operate(op, right));
};

/**
 * Visit UnaryOp.
 */

Evaluator.prototype.visitUnaryOp = function(unary){
  var op = unary.op
    , node = this.visit(unary.expr).first;

  if ('!' != op) utils.assertType(node, nodes.Unit);

  switch (op) {
    case '-':
      node.val = -node.val;
      break;
    case '+':
      node.val = +node.val;
      break;
    case '~':
      node.val = ~node.val;
      break;
    case '!':
      return node.toBoolean().negate();
  }

  return node;
};

/**
 * Visit TernaryOp.
 */

Evaluator.prototype.visitTernary = function(ternary){
  var ok = this.visit(ternary.cond).toBoolean();
  return nodes.true == ok
    ? this.visit(ternary.trueExpr)
    : this.visit(ternary.falseExpr);
};

/**
 * Visit Expression.
 */

Evaluator.prototype.visitExpression = function(expr){
  for (var i = 0, len = expr.nodes.length; i < len; ++i) {
    expr.nodes[i] = this.visit(expr.nodes[i]);
  }
  return expr;
};

/**
 * Visit Property.
 */

Evaluator.prototype.visitProperty = function(prop){
  var name = this.interpolate(prop)
    , fn = this.lookup(name)
    , call = fn instanceof nodes.Function
    , literal = ~this.calling.indexOf(name);

  // Function of the same name
  if (call && !literal && !prop.literal) {
    this.calling.push(name);
    var ret = this.visit(new nodes.Call(name, prop.expr));
    this.calling.pop();
    return ret;
  // Regular property
  } else {
    var _ = this.return;
    this.return = true;
    prop.expr = this.visit(prop.expr);
    prop.name = name;
    prop.literal = true;
    this.return = _;
    return prop;
  }
};

/**
 * Visit Root.
 */

Evaluator.prototype.visitRoot = function(block){
  for (var i = 0; i < block.nodes.length; ++i) {
    block.index = this.rootIndex = i;
    block.nodes[i] = this.visit(block.nodes[i]);
  }
  return block;
};

/**
 * Visit Block.
 */

Evaluator.prototype.visitBlock = function(block){
  this.stack.push(new Frame(block));
  for (var i = 0; i < block.nodes.length; ++i) {
    block.index = i;
    try {
      block.nodes[i] = this.visit(block.nodes[i]);
    } catch (err) {
      if (err instanceof nodes.Return) {
        if (this.return) {
          this.stack.pop();
          throw err;
        } else {
          block.nodes[i] = err;
          break;
        }
      } else {
        throw err;
      }
    }
  }
  this.stack.pop();
  return block;
};

/**
 * Visit If.
 */

Evaluator.prototype.visitIf = function(node){
  var ret
    , _ = this.return
    , block = this.currentBlock
    , negate = node.negate;

  this.return = true;
  var ok = this.visit(node.cond).first.toBoolean();
  this.return = _;

  // Evaluate body
  if (negate) {
    // unless
    if (nodes.false == ok) {
      ret = this.visit(node.block);
    }
  } else {
    // if
    if (nodes.true == ok) {
      ret = this.visit(node.block);
    // else
    } else if (node.elses.length) {
      var elses = node.elses
        , len = elses.length;
      for (var i = 0; i < len; ++i) {
        // else if
        if (elses[i].cond) {
          if (nodes.true == this.visit(elses[i].cond).first.toBoolean()) {
            ret = this.visit(elses[i].block);
            break;
          }
        // else
        } else {
          ret = this.visit(elses[i]);
        }
      }
    }
  }

  // mixin conditional statements within a selector group
  if (ret && !node.postfix && block.node && 'group' == block.node.nodeName) {
    this.mixin(ret.nodes, block);
    return nodes.null;
  }

  return ret || nodes.null;
};

/**
 * Visit Import.
 */

Evaluator.prototype.visitImport = function(import){
  var found
    , root = this.root
    , i = this.rootIndex
    , stylus = require('../stylus')
    , path = this.visit(import.path).first
    , relative = this.importPath;

  // Enusre string
  if (!path.string) throw new Error('@import string expected');
  var name = path = path.string;

  // Literal
  if (/\.css$/.test(path)) return import;
  path += '.styl';

  // Lookup
  if (relative) this.paths.push(relative);
  found = utils.lookup(path, this.paths, this.filename);
  found = found || utils.lookup(name + '/index.styl', this.paths, this.filename);
  if (relative) this.paths.pop();

  // Expose imports
  import.path = found;
  if (this.options._imports) this.options._imports.push(import);

  // Throw if import failed
  if (!found) throw new Error('failed to locate @import file ' + path);
  this.importPath = dirname(found);

  // Parse the file
  var str = fs.readFileSync(found, 'utf8')
    , rest = root.nodes.splice(++i, root.nodes.length);

  stylus.parse(str, {
      filename: found
    , root: root
  });

  rest.forEach(function(node){
    root.push(node);
  });

  return nodes.null;
};

/**
 * Invoke `fn` with `args`.
 *
 * @param {Function} fn
 * @param {Array} args
 * @return {Node}
 * @api private
 */

Evaluator.prototype.invokeFunction = function(fn, args){
  var block = new nodes.Block(fn.block.parent);
  fn.block.parent = block;

  // Clone the function body
  // to prevent mutation of subsequent calls
  // inject argument scope
  var body = fn.block.clone();

  // mixin block
  var mixinBlock = this.stack.currentFrame.block;

  // new block scope
  this.stack.push(new Frame(block));
  var scope = this.currentScope;

  // arguments local
  scope.add(new nodes.Ident('arguments', args));

  // mixin scope introspection
  scope.add(new nodes.Ident('mixin', this.return
    ? nodes.false
    : new nodes.String(mixinBlock.nodeName)));

  // inject arguments as locals
  fn.params.nodes.forEach(function(node, i){
    // rest param support
    if (node.rest) {
      node.val = new nodes.Expression;
      for (var len = args.nodes.length; i < len; ++i) {
        node.val.push(args.nodes[i]);
      }
      node.val.preserve = true;
    // argument default support
    } else {
      var arg = args.nodes[i];
      var val = arg && !arg.isEmpty
        ? args.nodes[i]
        : node.val;
      node = node.clone();
      node.val = val;
      // required argument not satisfied
      if (node.val instanceof nodes.Null) {
        throw new Error('argument ' + node + ' required for ' + fn);
      }
    }

    scope.add(node);
  });

  // invoke
  return this.invoke(body, true);
};

/**
 * Invoke built-in `fn` with `args`.
 *
 * @param {Function} fn
 * @param {Array} args
 * @return {Node}
 * @api private
 */

Evaluator.prototype.invokeBuiltin = function(fn, args){
  // Map arguments to first node
  // providing a nicer js api for
  // BIFs. Functions may specify that
  // they wish to accept full expressions
  // via .raw
  if (fn.raw) {
    args = args.nodes;
  } else {
    args = args.nodes.map(function(node){
      return node.first;
    });
  }

  // Invoke the BIF
  var body = fn.apply(this, args);

  // Always wrapping allows js functions
  // to return several values with a single
  // Expression node
  var expr = new nodes.Expression;
  expr.push(body);
  body = expr;

  // Invoke
  return this.invoke(body);
};

/**
 * Invoke the given function `body`.
 *
 * @param {Block} body
 * @return {Node}
 * @api private
 */

Evaluator.prototype.invoke = function(body, stack){
  var self = this
    , ret;

  // Return
  if (this.return) {
    ret = this.eval(body.nodes);
    if (stack) this.stack.pop();
  // Mixin
  } else {
    body = this.visit(body);
    if (stack) this.stack.pop();
    this.mixin(body.nodes, this.currentBlock);
    ret = nodes.null;
  }

  return ret;
};

/**
 * Mixin the given `nodes` to the given `block`.
 *
 * @param {Array} nodes
 * @param {Block} block
 * @api private
 */

Evaluator.prototype.mixin = function(nodes, block){
  var len = block.nodes.length
    , head = block.nodes.slice(0, block.index)
    , tail = block.nodes.slice(block.index + 1, len);
  this._mixin(nodes, head);
  block.nodes = head.concat(tail);
};

/**
 * Mixin the given `nodes` to the `dest` array.
 *
 * @param {Array} nodes
 * @param {Array} dest
 * @api private
 */

Evaluator.prototype._mixin = function(nodes, dest){
  var node
    , len = nodes.length;
  for (var i = 0; i < len; ++i) {
    switch ((node = nodes[i]).nodeName) {
      case 'return':
        return;
      case 'block':
        this._mixin(node.nodes, dest);
        break;
      default:
        dest.push(node);
    }
  }
};

/**
 * Evaluate the given `vals`.
 *
 * @param {Array} vals
 * @return {Node}
 * @api private
 */

Evaluator.prototype.eval = function(vals){
  if (!vals) return nodes.null;
  var len = vals.length
    , node = nodes.null;

  try {
    for (var i = 0; i < len; ++i) {
      node = vals[i];
      switch (node.nodeName) {
        case 'if':
          if ('block' != node.block.nodeName) {
            node = this.visit(node);
            break;
          }
        case 'each':
        case 'block':
          node = this.visit(node);
          if (node.nodes) node = this.eval(node.nodes);
          break;
        default:
          node = this.visit(node);
      }
    }
  } catch (err) {
    if (err instanceof nodes.Return) {
      return err.expr;
    } else {
      throw err;
    }
  }

  return node;
};

/**
 * Literal function `call`.
 *
 * @param {Call} call
 * @return {call}
 * @api private
 */

Evaluator.prototype.literalCall = function(call){
  call.args = this.visit(call.args);
  return call;
};

/**
 * Lookup `name`, with support for JavaScript
 * functions, and BIFs.
 *
 * @param {String} name
 * @return {Node}
 * @api private
 */

Evaluator.prototype.lookup = function(name){
  var val;
  if (val = this.stack.lookup(name)) {
    return utils.unwrap(val);
  } else {
    return this.lookupFunction(name);
  }
};

/**
 * Map segments in `node` returning a string.
 *
 * @param {Node} node
 * @return {String}
 * @api private
 */

Evaluator.prototype.interpolate = function(node){
  var self = this;
  return node.segments.map(function(node){
    function toString(node) {
      switch (node.nodeName) {
        case 'function':
        case 'ident':
          return node.name;
        case 'literal':
        case 'string':
        case 'unit':
          return node.val;
        case 'expression':
          var _ = self.return;
          self.return = true;
          var ret = toString(self.visit(node).first);
          self.return = _;
          return ret;
      }
    }
    return toString(node);
  }).join('');
};

/**
 * Lookup JavaScript user-defined or built-in function.
 *
 * @param {String} name
 * @return {Function}
 * @api private
 */

Evaluator.prototype.lookupFunction = function(name){
  var fn = this.functions[name] || bifs[name];
  if (fn) return new nodes.Function(name, fn);
};

/**
 * Check if the given `node` is an ident, and if it is defined.
 *
 * @param {Node} node
 * @return {Boolean}
 * @api private
 */

Evaluator.prototype.isDefined = function(node){
  if (node instanceof nodes.Ident) {
    return nodes.Boolean(this.lookup(node.name));
  } else {
    throw new Error('invalid "is defined" check on non-variable ' + node);
  }
};

/**
 * Warn with the given `msg`.
 *
 * @param {String} msg
 * @api private
 */

Evaluator.prototype.warn = function(msg){
  if (!this.warnings) return;
  console.warn('\033[33mWarning:\033[0m ' + msg);
};

/**
 * Return the current `Block`.
 *
 * @return {Block}
 * @api private
 */

Evaluator.prototype.__defineGetter__('currentBlock', function(){
  return this.stack.currentFrame.block;
});

/**
 * Return the current frame `Scope`.
 *
 * @return {Scope}
 * @api private
 */

Evaluator.prototype.__defineGetter__('currentScope', function(){
  return this.stack.currentFrame.scope;
});

/**
 * Return the current `Frame`.
 *
 * @return {Frame}
 * @api private
 */

Evaluator.prototype.__defineGetter__('currentFrame', function(){
  return this.stack.currentFrame;
});


text:
"use strict";

require("conventional-changelog")({
    from: "87d54082c4052a44b5591567fc677e9cb2f7dee0",
    repository: "https://github.com/BrowserSync/browser-sync",
    version: require("./package.json").version
}, function (err, log) {
    require("fs").writeFileSync("./CHANGELOG.md", log);
});


text:
<!DOCTYPE html>

    <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>the broken arrow complex</title>
<meta name="description" content="we are the broken arrow complex">

<meta property="og:url" content="" />
<meta property="og:type" content="website" />
<meta property="og:title" content="the broken arrow complex" />
<meta property="og:description" content="we are the broken arrow complex" />
<meta property="og:image" content="" />

<meta name="google-site-verification" content="4J-uK3Cby3I_iH2MhcUVAPZKJd1Jpu0hwRiZUlq9Bkc" />

<link rel="apple-touch-icon" sizes="57x57" href="/public/images/icon/apple-icon-57x57.png">
<link rel="apple-touch-icon" sizes="60x60" href="/public/images/icon/apple-icon-60x60.png">
<link rel="apple-touch-icon" sizes="72x72" href="/public/images/icon/apple-icon-72x72.png">
<link rel="apple-touch-icon" sizes="76x76" href="/public/images/icon/apple-icon-76x76.png">
<link rel="apple-touch-icon" sizes="114x114" href="/public/images/icon/apple-icon-114x114.png">
<link rel="apple-touch-icon" sizes="120x120" href="/public/images/icon/apple-icon-120x120.png">
<link rel="apple-touch-icon" sizes="144x144" href="/public/images/icon/apple-icon-144x144.png">
<link rel="apple-touch-icon" sizes="152x152" href="/public/images/icon/apple-icon-152x152.png">
<link rel="apple-touch-icon" sizes="180x180" href="/public/images/icon/apple-icon-180x180.png">
<link rel="icon" type="image/png" sizes="192x192"  href="/public/images/icon/android-icon-192x192.png">
<link rel="icon" type="image/png" sizes="32x32" href="/public/images/icon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="96x96" href="/public/images/icon/favicon-96x96.png">
<link rel="icon" type="image/png" sizes="16x16" href="/public/images/icon/favicon-16x16.png">
<link rel="manifest" href="/public/images/icon/manifest.json">
<meta name="msapplication-TileColor" content="#ffffff">
<meta name="msapplication-TileImage" content="/public/images/icon/ms-icon-144x144.png">
<meta name="theme-color" content="#ffffff">

<link href="public/css/main.css" media="screen" rel="stylesheet">

  THIS IS SOMETHING ELSE

      This is a really big deal :

this is !important

end

      this is the test page

    this is the test layout