public static NodeList <CartoSelector> GetCartoSelectors(this Ruleset ruleset)
        {
            NodeList <CartoSelector> selectors = new NodeList <CartoSelector>();

            foreach (Selector selector in ruleset.Selectors)
            {
                CartoSelector cs = selector as CartoSelector;
                if (cs)
                {
                    selectors.Add(cs);
                }
            }

            return(selectors);
        }
        public static List <CartoDefinition> Flatten(this Ruleset ruleset, List <CartoDefinition> result, NodeList <CartoSelector> parents, Env env, Logging.Logger logger)
        {
            NodeList <CartoSelector> selectors       = GetCartoSelectors(ruleset);
            NodeList <CartoSelector> selectorsResult = new NodeList <CartoSelector>();

            if (selectors.Count == 0)
            {
                env.Frames.Concat(ruleset.Rules);
            }

            // evaluate zoom variables on this object.
            EvaluateZooms(selectors, env);

            for (int i = 0; i < selectors.Count; i++)
            {
                CartoSelector child = selectors[i];
                if (child.Filters == null)
                {
                    // TODO: is this internal inconsistency?
                    // This is an invalid filterset.

                    continue;
                }

                if (parents.Count > 0)
                {
                    foreach (CartoSelector parent in parents)
                    {
                        object mergedFilters = parent.Filters.CloneWith(child.Filters, env);                        //new CartoFilterSet(child.Filters);

                        if (mergedFilters == null)
                        {
                            // Filters could be added, but they didn't change the
                            // filters. This means that we only have to clone when
                            // the zoom levels or the attachment is different too.

                            if (parent.Zoom == (parent.Zoom & child.Zoom) && parent.Attachment == child.Attachment && parent.ElementsEqual(child))
                            {
                                selectorsResult.Add(parent);
                                continue;
                            }
                            else
                            {
                                mergedFilters = parent.Filters;
                            }
                        }
                        else if (mergedFilters is bool && !(Convert.ToBoolean(mergedFilters)))
                        {
                            // The merged filters are invalid, that means we don't
                            // have to clone.

                            continue;
                        }

                        CartoSelector clone = new CartoSelector(child);
                        clone.Filters = (CartoFilterSet)mergedFilters;
                        clone.Zoom    = parent.Zoom & child.Zoom;
                        clone.Elements.Clear();
                        clone.Elements.AddRange(parent.Elements.Concat(child.Elements));

                        if (parent.Attachment != null && child.Attachment != null)
                        {
                            clone.Attachment = parent.Attachment + '/' + child.Attachment;
                        }
                        else
                        {
                            clone.Attachment = child.Attachment ?? parent.Attachment;
                        }

                        clone.Conditions = parent.Conditions + child.Conditions;
                        clone.Index      = child.Index;
                        selectorsResult.Add(clone);
                    }
                }
                else
                {
                    selectorsResult.Add(child);
                }
            }

            NodeList <CartoRule> rules = new NodeList <CartoRule>();

            foreach (dotless.Core.Parser.Infrastructure.Nodes.Node rule in ruleset.Rules)
            {
                // Recursively flatten any nested rulesets
                if (rule is Ruleset)
                {
                    List <CartoDefinition> defs = Flatten(rule as Ruleset, result, selectorsResult, env, logger);
                }
                else if (rule is CartoRule)
                {
                    rules.Add(rule as CartoRule);
                }
                else if (rule as CartoInvalidElement)
                {
                    CartoInvalidElement cie = rule as CartoInvalidElement;
                    env.Logger.Log(dotless.Core.Loggers.LogLevel.Error, "Rule");
                    Logging.LogFactory.WriteLogEntry(logger, new ParsingException(cie.Value, cie.Location.FileName, Zone.GetLineNumber(cie.Location)));
                }
            }

            int index = rules.Count > 0 ? rules[0].Index : -1;

            for (int i = 0; i < selectorsResult.Count; i++)
            {
                // For specificity sort, use the position of the first rule to allow
                // defining attachments that are under current element as a descendant
                // selector.

                CartoSelector selector = selectorsResult[i];
                if (index >= 0)
                {
                    selector.Index = index;
                }

                // make a copy of the rules array, since the next iteration will change zooms
                CartoRule[] arrRules = new CartoRule[rules.Count];
                for (int j = 0; j < arrRules.Length; j++)
                {
                    arrRules[j] = (CartoRule)rules[j].Clone();
                }

                result.Add(new CartoDefinition(selector, arrRules));
            }

            return(result);
        }