<commit_before>// Copyright 2010, 2011 The Apache Software Foundation
//
// Licensed 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.tapestry5.internal.transform;

import java.util.Locale;

import org.apache.tapestry5.Asset;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.SetupRender;
import org.apache.tapestry5.func.F;
import org.apache.tapestry5.func.Flow;
import org.apache.tapestry5.func.Mapper;
import org.apache.tapestry5.func.Worker;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.services.SymbolSource;
import org.apache.tapestry5.model.MutableComponentModel;
import org.apache.tapestry5.plastic.FieldHandle;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodInvocation;
import org.apache.tapestry5.plastic.PlasticClass;
import org.apache.tapestry5.plastic.PlasticMethod;
import org.apache.tapestry5.services.AssetSource;
import org.apache.tapestry5.services.TransformConstants;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
import org.apache.tapestry5.services.transform.TransformationSupport;

/**
 * Implements the {@link Import} annotation, both at the class and at the method level.
 * 
 * @since 5.2.0
 */
public class ImportWorker implements ComponentClassTransformWorker2
{
    private final JavaScriptSupport javascriptSupport;

    private final SymbolSource symbolSource;

    private final AssetSource assetSource;

    private final Worker<Asset> importLibrary = new Worker<Asset>()
    {
        public void work(Asset asset)
        {
            javascriptSupport.importJavaScriptLibrary(asset);
        }
    };

    private final Worker<Asset> importStylesheet = new Worker<Asset>()
    {
        public void work(Asset asset)
        {
            javascriptSupport.importStylesheet(asset);
        };
    };

    public ImportWorker(JavaScriptSupport javascriptSupport, SymbolSource symbolSource, AssetSource assetSource)
    {
        this.javascriptSupport = javascriptSupport;
        this.symbolSource = symbolSource;
        this.assetSource = assetSource;
    }

    public void transform(PlasticClass componentClass, TransformationSupport support, MutableComponentModel model)
    {
        processClassAnnotationAtSetupRenderPhase(componentClass, model);

        for (PlasticMethod m : componentClass.getMethodsWithAnnotation(Import.class))
        {
            decorateMethod(componentClass, model, m);
        }
    }

    private void processClassAnnotationAtSetupRenderPhase(PlasticClass componentClass, MutableComponentModel model)
    {
        Import annotation = componentClass.getAnnotation(Import.class);

        if (annotation == null)
            return;

        PlasticMethod setupRender = componentClass.introduceMethod(TransformConstants.SETUP_RENDER_DESCRIPTION);

        decorateMethod(componentClass, model, setupRender, annotation);

        model.addRenderPhase(SetupRender.class);
    }

    private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method)
    {
        Import annotation = method.getAnnotation(Import.class);

        decorateMethod(componentClass, model, method, annotation);
    }

    private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method,
            Import annotation)
    {
        importStacks(method, annotation.stack());

        importLibraries(componentClass, model, method, annotation.library());

        importStylesheets(componentClass, model, method, annotation.stylesheet());
    }

    private void importStacks(PlasticMethod method, String[] stacks)
    {
        if (stacks.length != 0)
            method.addAdvice(createImportStackAdvice(stacks));
    }

    private MethodAdvice createImportStackAdvice(final String[] stacks)
    {
        return new MethodAdvice()
        {
            public void advise(MethodInvocation invocation)
            {
                for (String stack : stacks)
                {
                    javascriptSupport.importStack(stack);

                    invocation.proceed();
                }
            }
        };
    }

    private void importLibraries(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
            String[] paths)
    {
        decorateMethodWithOperation(plasticClass, model, method, paths, importLibrary);
    }

    private void importStylesheets(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
            String[] paths)
    {
        decorateMethodWithOperation(plasticClass, model, method, paths, importStylesheet);
    }

    private void decorateMethodWithOperation(PlasticClass componentClass, MutableComponentModel model,
            PlasticMethod method, String[] paths, Worker<Asset> operation)
    {
        if (paths.length == 0)
            return;

        String[] expandedPaths = expandPaths(paths);

        FieldHandle assetListHandle = componentClass.introduceField(Asset[].class,
                "importedAssets_" + method.getDescription().methodName).getHandle();

        storeLocalizedAssetsAtPageLoad(componentClass, model.getBaseResource(), expandedPaths, assetListHandle);

        addMethodAssetOperationAdvice(method, assetListHandle, operation);
    }

    private String[] expandPaths(String[] paths)
    {
        String[] result = new String[paths.length];

        for (int i = 0; i < paths.length; i++)
            result[i] = symbolSource.expandSymbols(paths[i]);

        return result;
    }

    private void storeLocalizedAssetsAtPageLoad(PlasticClass componentClass, final Resource baseResource,
            final String[] expandedPaths, final FieldHandle access)
    {
        MethodAdvice advice = new MethodAdvice()
        {
            public void advise(MethodInvocation invocation)
            {
                invocation.proceed();

                ComponentResources resources = invocation.getInstanceContext().get(ComponentResources.class);

                Asset[] assets = convertPathsToAssetArray(baseResource, resources.getLocale(), expandedPaths);

                access.set(invocation.getInstance(), assets);
            }
        };

        componentClass.introduceMethod(TransformConstants.CONTAINING_PAGE_DID_LOAD_DESCRIPTION).addAdvice(advice);
    }
<commit_msg>TAP5-1508: Optimize the @Import worker to initialize assets in the constructor, not on page load<commit_after>// Copyright 2010, 2011 The Apache Software Foundation
//
// Licensed 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.tapestry5.internal.transform;

import java.util.Locale;

import org.apache.tapestry5.Asset;
import org.apache.tapestry5.ComponentResources;
import org.apache.tapestry5.annotations.Import;
import org.apache.tapestry5.annotations.SetupRender;
import org.apache.tapestry5.func.F;
import org.apache.tapestry5.func.Mapper;
import org.apache.tapestry5.func.Worker;
import org.apache.tapestry5.ioc.Resource;
import org.apache.tapestry5.ioc.services.SymbolSource;
import org.apache.tapestry5.model.MutableComponentModel;
import org.apache.tapestry5.plastic.ComputedValue;
import org.apache.tapestry5.plastic.FieldHandle;
import org.apache.tapestry5.plastic.InstanceContext;
import org.apache.tapestry5.plastic.MethodAdvice;
import org.apache.tapestry5.plastic.MethodInvocation;
import org.apache.tapestry5.plastic.PlasticClass;
import org.apache.tapestry5.plastic.PlasticField;
import org.apache.tapestry5.plastic.PlasticMethod;
import org.apache.tapestry5.services.AssetSource;
import org.apache.tapestry5.services.TransformConstants;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;
import org.apache.tapestry5.services.transform.ComponentClassTransformWorker2;
import org.apache.tapestry5.services.transform.TransformationSupport;

/**
 * Implements the {@link Import} annotation, both at the class and at the method level.
 * 
 * @since 5.2.0
 */
public class ImportWorker implements ComponentClassTransformWorker2
{
    private final JavaScriptSupport javascriptSupport;

    private final SymbolSource symbolSource;

    private final AssetSource assetSource;

    private final Worker<Asset> importLibrary = new Worker<Asset>()
    {
        public void work(Asset asset)
        {
            javascriptSupport.importJavaScriptLibrary(asset);
        }
    };

    private final Worker<Asset> importStylesheet = new Worker<Asset>()
    {
        public void work(Asset asset)
        {
            javascriptSupport.importStylesheet(asset);
        };
    };

    public ImportWorker(JavaScriptSupport javascriptSupport, SymbolSource symbolSource, AssetSource assetSource)
    {
        this.javascriptSupport = javascriptSupport;
        this.symbolSource = symbolSource;
        this.assetSource = assetSource;
    }

    public void transform(PlasticClass componentClass, TransformationSupport support, MutableComponentModel model)
    {
        processClassAnnotationAtSetupRenderPhase(componentClass, model);

        for (PlasticMethod m : componentClass.getMethodsWithAnnotation(Import.class))
        {
            decorateMethod(componentClass, model, m);
        }
    }

    private void processClassAnnotationAtSetupRenderPhase(PlasticClass componentClass, MutableComponentModel model)
    {
        Import annotation = componentClass.getAnnotation(Import.class);

        if (annotation == null)
            return;

        PlasticMethod setupRender = componentClass.introduceMethod(TransformConstants.SETUP_RENDER_DESCRIPTION);

        decorateMethod(componentClass, model, setupRender, annotation);

        model.addRenderPhase(SetupRender.class);
    }

    private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method)
    {
        Import annotation = method.getAnnotation(Import.class);

        decorateMethod(componentClass, model, method, annotation);
    }

    private void decorateMethod(PlasticClass componentClass, MutableComponentModel model, PlasticMethod method,
            Import annotation)
    {
        importStacks(method, annotation.stack());

        importLibraries(componentClass, model, method, annotation.library());

        importStylesheets(componentClass, model, method, annotation.stylesheet());
    }

    private void importStacks(PlasticMethod method, String[] stacks)
    {
        if (stacks.length != 0)
            method.addAdvice(createImportStackAdvice(stacks));
    }

    private MethodAdvice createImportStackAdvice(final String[] stacks)
    {
        return new MethodAdvice()
        {
            public void advise(MethodInvocation invocation)
            {
                for (String stack : stacks)
                {
                    javascriptSupport.importStack(stack);

                    invocation.proceed();
                }
            }
        };
    }

    private void importLibraries(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
            String[] paths)
    {
        decorateMethodWithOperation(plasticClass, model, method, paths, importLibrary);
    }

    private void importStylesheets(PlasticClass plasticClass, MutableComponentModel model, PlasticMethod method,
            String[] paths)
    {
        decorateMethodWithOperation(plasticClass, model, method, paths, importStylesheet);
    }

    private void decorateMethodWithOperation(PlasticClass componentClass, MutableComponentModel model,
            PlasticMethod method, String[] paths, Worker<Asset> operation)
    {
        if (paths.length == 0)
            return;

        String[] expandedPaths = expandPaths(paths);

        PlasticField assetListField = componentClass.introduceField(Asset[].class,
                "importedAssets_" + method.getDescription().methodName);

        initializeAssetsFromPaths(componentClass, model.getBaseResource(), expandedPaths, assetListField);

        addMethodAssetOperationAdvice(method, assetListField.getHandle(), operation);
    }

    private String[] expandPaths(String[] paths)
    {
        String[] result = new String[paths.length];

        for (int i = 0; i < paths.length; i++)
            result[i] = symbolSource.expandSymbols(paths[i]);

        return result;
    }

    private void initializeAssetsFromPaths(PlasticClass componentClass, final Resource baseResource,
            final String[] expandedPaths, final PlasticField assetsField)
    {
        assetsField.injectComputed(new ComputedValue<Asset[]>()
        {
            public Asset[] get(InstanceContext context)
            {
                ComponentResources resources = context.get(ComponentResources.class);

                return convertPathsToAssetArray(baseResource, resources.getLocale(), expandedPaths);
            }
        });
    }

{'fraction_non_alphanumeric': 0.08209382151029748, 'fraction_numerical': 0.006507437070938215, 'mean_word_length': 3.378522229179712, 'pattern_counts': {'":': 0, '<': 16, '<?xml version=': 0, '>': 14, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4503836', 'n_tokens_mistral': 3683, 'n_tokens_neox': 3456, 'n_words': 839}