/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License"); you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.sling.contextaware.config.resource.impl.def;

import java.util.Collections;
import java.util.Iterator;
import java.util.NoSuchElementException;

import org.apache.sling.api.resource.Resource;
import org.apache.sling.contextaware.config.resource.spi.ContextPathStrategy;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@Component(service = ContextPathStrategy.class)
@Designate(ocd=DefaultContextPathStrategy.Config.class)
public class DefaultContextPathStrategy implements ContextPathStrategy {

    @ObjectClassDefinition(name="Apache Sling Context-Aware Default Context Path Strategy",
            description="Detects context path by existence of " + PROPERTY_CONFIG + " properties.")
    static @interface Config {
        
        @AttributeDefinition(name="Enabled",
                      description = "Enable this context path strategy.")
        boolean enabled() default true;
        
    }
    
    /**
     * Property that points to the configuration to be used.
     * Additionally each resource having this property marks the beginning of a new context sub-tree.
     */
    public static final String PROPERTY_CONFIG = "sling:config-ref";
   
    private final Logger log = LoggerFactory.getLogger(this.getClass());
    
    private volatile Config config;
    
    @Activate
    private void activate(ComponentContext componentContext, Config config) {
        this.config = config; 
    }
    
    @Override
    public Iterator<Resource> findContextResources(Resource resource) {
        if (!config.enabled()) {
            return Collections.emptyIterator();
        }
        return new ConfigResourceIterator(resource);
    }
    
    /**
     * Searches the resource hierarchy upwards for all context and returns the root resource for each of them.
     */
    private class ConfigResourceIterator implements Iterator<Resource> {

        private Resource next;
        
        public ConfigResourceIterator(Resource startResource) {
            next = findNextContextResource(startResource);
        }
        
        @Override
        public boolean hasNext() {
            return next != null;
        }

        @Override
        public Resource next() {
            if (next == null) {
                throw new NoSuchElementException();
            }
            Resource result = next;
            next = findNextContextResource(next.getParent());
            return result;
        }
        
        @Override
        public void remove() {
            throw new UnsupportedOperationException();
        }

        /**
         * Find next configuration context root for given resource.
         * @param startResource Resource to start searching
         * @return Next resource with sling:config-ref property or null if none found.
         */
        private Resource findNextContextResource(Resource startResource) {
            // start at resource, go up
            Resource resource = startResource;
            
            while (resource != null) {
                if (hasConfigRef(resource)) {
                    log.trace("Found context path '{}'.", resource.getPath());
                    return resource;
                }
                // if getParent() returns null, stop
                resource = resource.getParent();
            }

            // if hit root and nothing found, return null
            return null;
        }
        
        private boolean hasConfigRef(final Resource resource) {
            return resource.getValueMap().get(PROPERTY_CONFIG, String.class) != null;
        }

    }

}

@@ -1094,60 +1094,8 @@
 gy;%0A
-import org.osgi.service.component.ComponentContext;%0A
 impo
@@ -1142,24 +1142,24 @@
 s.Activate;%0A
+
 import org.o
@@ -1825,16 +1825,23 @@
 .%22)%0A    
+public 
 static @
@@ -1859,24 +1859,16 @@
 onfig %7B%0A
-        
 %0A       
@@ -2023,28 +2023,16 @@
 ue;%0A
-        
 %0A
+
     %7D%0A
-    
 %0A
+
     
@@ -2275,20 +2275,17 @@
 g-ref%22;%0A
-   
 %0A
+
     priv
@@ -2349,21 +2349,17 @@
 ass());%0A
-    
 %0A
+
     priv
@@ -2386,21 +2386,17 @@
 config;%0A
-    
 %0A
+
     @Act
@@ -2431,43 +2431,8 @@
 ate(
-ComponentContext componentContext, 
 Conf
@@ -2477,21 +2477,16 @@
 fig;
- 
 %0A    %7D%0A
-    
 %0A
+
     
@@ -2717,21 +2717,17 @@
 ;%0A    %7D%0A
-    
 %0A
+
     /**%0A
@@ -2950,25 +2950,17 @@
 e next;%0A
-        
 %0A
+
         
@@ -3080,33 +3080,25 @@
 ;%0A        %7D%0A
-        
 %0A
+
         @Ove
@@ -3462,33 +3462,25 @@
 ;%0A        %7D%0A
-        
 %0A
+
         @Ove
@@ -3987,21 +3987,9 @@
 ce;%0A
-            
 %0A
+
     
@@ -4413,24 +4413,16 @@
       %7D%0A
-        
 %0A       

Clean up code, make Config class public for easier testing