text:
author: jlordiales
comments: true
share: true
date: 2012-12-26
layout: post
slug: static-factory-methods-vs-traditional-constructors
title: Static factory methods vs traditional constructors
categories:
- Best Practices
- Java
tags:
- builder
- constructors
- factory
- interface-based API
- java
- patterns
- static
- type inference
---

I've previously talked a little bit about the
[Builder Pattern]({% post_url 2012-12-13-the-builder-pattern-in-practice %}),
a useful pattern to instantiate classes with several (possibly optional)
attributes that results in easier to read, write and maintain client code, among
other benefits. Today, I'm going to continue exploring object creation
techniques but this time for a more general case.

Take the following example, which is by no means a useful class other than to
make my point. We have a _RandomIntGenerator_ class that, as the name suggests,
generates random int numbers. Something like:

{% highlight java %}
public class RandomIntGenerator {
  private final int min;
  private final int max;

  public int next() {...}
}
{% endhighlight %}

Our generator takes a minimum and maximum and then generates random numbers
between those 2 values. Notice that the two attributes are declared _final_ so
we have to initialize them either on their declaration or in the class
constructor. Let's go with the constructor:

{% highlight java %}
public RandomIntGenerator(int min, int max) {
  this.min = min;
  this.max = max;
}
{% endhighlight %}

Now, we also want to give our clients the possibility to specify just a minimum
value and then generate random values between that minimum and the max possible
value for ints. So we add a second constructor:

{% highlight java %}
public RandomIntGenerator(int min) {
  this.min = min;
  this.max = Integer.MAX_VALUE;
}
{% endhighlight %}

So far so good, right? But in the same way that we provided a constructor to
just specify the minimum value, we want to do the same for just the maximum.
We'll just add a third constructor like:

{% highlight java %}
public RandomIntGenerator(int max) {
  this.min = Integer.MIN_VALUE;
  this.max = max;
}
{% endhighlight %}

If you try that, you'll get a compilation error that goes: _Duplicate method
RandomIntGenerator(int) in type RandomIntGenerator_. What's wrong?

The problem is that constructors, by definition, have no names. As such, a class
can only have one constructor with a given signature in the same way that you
can't have two methods with the same signature (same return type, name and
parameters type). That is why when we tried to add the _RandomIntGenerator(int
max)_ constructor we got that compilation error, because we already had the
_RandomIntGenerator(int min)_ one.

Is there something we can do in cases like this one? Not with constructors but
fortunately there's something else we can use: **static factory methods**, which
are simply public static methods that return an instance of the class. You've
probably used this technique without even realizing it. Have you ever used
_Boolean.valueOf_? It looks something like:

{% highlight java %}
public static Boolean valueOf(boolean b) {
  return (b ? TRUE : FALSE);
}
{% endhighlight %}

Applying static factories to our `RandomIntGenerator` example, we could get:

{% highlight java %}
public class RandomIntGenerator {
  private final int min;
  private final int max;

  private RandomIntGenerator(int min, int max) {
    this.min = min;
    this.max = max;
  }

  public static RandomIntGenerator between(int max, int min) {
    return new RandomIntGenerator(min, max);
  }

  public static RandomIntGenerator biggerThan(int min) {
    return new RandomIntGenerator(min, Integer.MAX_VALUE);
  }

  public static RandomIntGenerator smallerThan(int max) {
    return new RandomIntGenerator(Integer.MIN_VALUE, max);
  }

  public int next() {...}
}
{% endhighlight %}

Note how the constructor was made private to ensure that the class is only
instantiated through its public static factory methods. Also note how your
intent is clearly expressed when you have a client with
`RandomIntGenerator.between(10,20)` instead of `new RandomIntGenerator(10,20)`

It's worth mentioning that this technique is not the same as the Factory method
Design Pattern from the [Gang of
Four](http://en.wikipedia.org/wiki/Design_Patterns_(book)). Any class can
provide static factory methods instead of, or in addition to, constructors. So
what are the advantages and disadvantages of this technique?

We already mentioned the first advantage of static factory methods: unlike
constructors they have names. This has two direct consequences,

  1. We can provide a meaningful name for our constructors.

  2. We can provide several constructors with the same number and type of
     parameters, something that as we saw earlier we can't do with class
     constructors.

Another advantage of static factories is that, unlike constructors, they are not
required to return a new object every time they are invoked. This is extremely
useful when working with
[immutable classes]({% post_url 2012-12-24-the-ins-and-outs-of-immutability %})
to provide constant objects for common used values and avoid creating
unnecessary duplicate objects. The `Boolean.valueOf` code that I showed
previously illustrates this point perfectly. Notice that this static method
returns either `TRUE` or `FALSE`, both immutable Boolean objects.

A third advantage of static factory methods is that they can return an object of
any subtype of their return type. This gives you the possibility to change the
return type freely without affecting clients. Moreover, you can hide
implementation classes and have an [interface-based
API](http://en.wikipedia.org/wiki/Interface-based_programming), which is usually
a really good idea. But I think this can be better seen by an example.

Remember the `RandomIntGenerator` at the beginning of this post? Let's make that
a little bit more complicated. Imagine that we now want to provide random
generators not just for integers but for other data-types like String, Double or
Long. They are all going to have a `next()` method that returns a random object
of a particular type, so we could start with an interface like:

{% highlight java %}
public interface RandomGenerator<T> {
  T next();
}
{% endhighlight %}

Our first implementation of the `RandomIntGenerator` now becomes:

{% highlight java %}
class RandomIntGenerator implements RandomGenerator<Integer> {
  private final int min;
  private final int max;

  RandomIntGenerator(int min, int max) {
    this.min = min;
    this.max = max;
  }

  public Integer next() {...}
}
{% endhighlight %}

We could also have a String generator:

{% highlight java %}
class RandomStringGenerator implements RandomGenerator<String> {
  private final String prefix;

  RandomStringGenerator(String prefix) {
    this.prefix = prefix;
  }

  public String next() {...}
}
{% endhighlight %}

Notice how all the classes are declared package-private (default scope) and so
are their constructors. This means that no client outside of their package can
create instances of these generators. So what do we do? Tip: It starts with
"static" and ends with "methods".  Consider the following class:

{% highlight java %}
public final class RandomGenerators {
  // Suppresses default constructor, ensuring non-instantiability.
  private RandomGenerators() {}

  public static final RandomGenerator<Integer> getIntGenerator() {
    return new RandomIntGenerator(Integer.MIN_VALUE, Integer.MAX_VALUE);
  }

  public static final RandomGenerator<String> getStringGenerator() {
    return new RandomStringGenerator("");
  }
}
{% endhighlight %}

RandomGenerators` is just a noninstantiable utility class with nothing else than
static factory methods. Being on the same package as the different generators
this class can effectively access and instantiate those classes. But here comes
the interesting part. Note that the methods only return the `RandomGenerator`
interface, and that's all the clients need really. If they get a
`RandomGenerator<Integer>` they know that they can call `next()` and get a
random integer.  Imagine that next month we code a super efficient new integer
generator. Provided that this new class implements `RandomGenerator<Integer>` we
can change the return type of the static factory method and all clients are now
magically using the new implementation without them even noticing the change.

Classes like `RandomGenerators` are quite common both on the JDK and on third
party libraries. You can see examples in `Collections` (in java.util), `Lists`,
`Sets` or `Maps` in [Guava](http://code.google.com/p/guava-libraries/). The
naming convention is usually the same: if you have an interface named `Type` you
put your static factory methods in a noninstantiable class named `Types`.

A final advantage of static factories is that they make instantiating
parameterized classes a lot less verbose. Have you ever had to write code like
this?

{% highlight java %}
Map<String, List<String>> map = new HashMap<String, List<String>>();
{% endhighlight %}

You are repeating the same parameters twice on the same line of code. Wouldn't
it be nice if the right side of the assign could be _inferred_ from the left
side? Well, with static factories it can. The following code is taken from
Guava's `Maps` class:

{% highlight java %}
public static <K, V> HashMap<K, V> newHashMap() {
  return new HashMap<K, V>();
}
{% endhighlight %}

So now our client code becomes:

{% highlight java %}
Map<String, List<String>> map = Maps.newHashMap();
{% endhighlight %}

Pretty nice, isn't it? This capability is known as [Type
inference](http://docs.oracle.com/javase/tutorial/java/generics/genTypeInference.html).
It's worth mentioning that Java 7 introduced type inference through the use of
the [diamond operator](http://www.javaworld.com/community/node/7567). So if
you're using Java 7 you can write the previous example as:

{% highlight java %}
Map<String, List<String>> map = new HashMap<>();
{% endhighlight %}

The main disadvantage of static factories is that classes without public or
protected constructors cannot be extended. But this might be actually a good
thing in some cases because it encourages developers to [favor composition over
inheritance](http://en.wikipedia.org/wiki/Composition_over_inheritance).

To summarize, static factory methods provide a lot of benefits and just one
drawback that might actually not be a problem when you think about it.
Therefore, resist the urge to automatically provide public constructors and
evaluate if static factories are a better fit for your class.


text:
package com.google.template.soy.javagencode;

import static com.google.common.collect.ImmutableList.toImmutableList;
import static com.google.template.soy.base.SourceLocation.UNKNOWN;
import static com.google.template.soy.javagencode.javatypes.CodeGenUtils.ADD_TO_LIST_PARAM;
import static com.google.template.soy.javagencode.javatypes.CodeGenUtils.AS_RECORD;
import static com.google.template.soy.javagencode.javatypes.CodeGenUtils.INDIRECT_P;
import static com.google.template.soy.javagencode.javatypes.CodeGenUtils.INIT_LIST_PARAM;
import static com.google.template.soy.javagencode.javatypes.CodeGenUtils.INJECTED_P;
import static com.google.template.soy.javagencode.javatypes.CodeGenUtils.SET_PARAM_INTERNAL;
import static com.google.template.soy.javagencode.javatypes.CodeGenUtils.STANDARD_P;
import static com.google.template.soy.shared.internal.gencode.JavaGenerationUtils.appendFunctionCallWithParamsOnNewLines;
import static com.google.template.soy.shared.internal.gencode.JavaGenerationUtils.appendImmutableList;
import static com.google.template.soy.shared.internal.gencode.JavaGenerationUtils.appendImmutableMap;
import static com.google.template.soy.shared.internal.gencode.JavaGenerationUtils.appendJavadoc;
import static com.google.template.soy.shared.internal.gencode.JavaGenerationUtils.isReservedKeyword;
import static com.google.template.soy.shared.internal.gencode.JavaGenerationUtils.makeLowerCamelCase;
import static java.util.stream.Collectors.counting;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toList;

import com.google.common.base.Strings;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Streams;
import com.google.template.soy.base.internal.IndentedLinesBuilder;
import com.google.template.soy.error.ErrorReporter;
import com.google.template.soy.error.SoyErrorKind;
import com.google.template.soy.javagencode.KytheHelper.Span;
import com.google.template.soy.javagencode.SoyFileNodeTransformer.FileInfo;
import com.google.template.soy.javagencode.SoyFileNodeTransformer.ParamInfo;
import com.google.template.soy.javagencode.SoyFileNodeTransformer.ParamStatus;
import com.google.template.soy.javagencode.SoyFileNodeTransformer.TemplateInfo;
import com.google.template.soy.javagencode.javatypes.CodeGenUtils;
import com.google.template.soy.javagencode.javatypes.FutureJavaType;
import com.google.template.soy.javagencode.javatypes.JavaType;
import com.google.template.soy.javagencode.javatypes.RecordJavaType;
import com.google.template.soy.shared.internal.gencode.GeneratedFile;
import com.google.template.soy.soytree.AbstractSoyNodeVisitor;
import com.google.template.soy.soytree.FileSetMetadata;
import com.google.template.soy.soytree.SoyFileNode;
import com.google.template.soy.soytree.SoyFileSetNode;
import com.google.template.soy.soytree.SoyNode;
import com.google.template.soy.types.SoyTypeRegistry;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * Visitor for generating Java template parameter builders (see {@link
 * com.google.template.soy.data.BaseSoyTemplateImpl}) that can be used for invoking Soy templates
 * from Java. One java file will be generated for each soy file, containing template param builders
 * for each template in the soy file.
 *
 * <p>For example, "foo.soy" containing templates "bar" and "baz" would result in FooTemplates.java,
 * with inner classes Bar and Baz.
 */
public final class GenerateBuildersVisitor
    extends AbstractSoyNodeVisitor<ImmutableList<GeneratedFile>> {

  private static final String TEMPLATE_NAME_FIELD = "__NAME__";
  private static final String PARAMS_FIELD = "__PARAMS__";
  private static final String PROTOS_FIELD = "__PROTOS__";
  private static final String CSS_PROVIDES_FIELD = "__PROVIDED_CSS__";
  private static final String CSS_MAP_FIELD = "__PROVIDED_CSS_MAP__";
  private static final String DEFAULT_INSTANCE_FIELD = "__DEFAULT_INSTANCE__";

  private static final SoyErrorKind TYPE_COLLISION =
      SoyErrorKind.of(
          "Parameter ''{0}'' in {1} has different types in different templates. No parameter"
              + " setter generated.");
  private static final SoyErrorKind INDIRECT_PROTO =
      SoyErrorKind.of(
          "Indirect parameter ''{0}'' in {1} is of type proto or proto enum. No parameter setter"
              + " generated.");
  private static final SoyErrorKind TEMPLATE_NAME_COLLISION =
      SoyErrorKind.of(
          "When generating Soy Java Template Builders, the template: {0} generated the same Java"
              + " UpperCamelCase name as another template in this file, or collided with a"
              + " reserved identifier: "
              + SoyFileNodeTransformer.RESERVED_IDENTIFIERS
              + ". This template was skipped during Soy java_builders generation. To use this API,"
              + " all Soy template names in a given file should be unique when converted to"
              + " UpperCamelCase (with non-alphanumeric characters stripped). The generated Java"
              + " class name was: {1}.");
  private static final SoyErrorKind PARAM_NAME_COLLISION =
      SoyErrorKind.of(
          "When generating Soy Java Template Builders, the param named {0} in template {1}"
              + " generated the same UpperCamelCase name as another parameter, or collided with"
              + " a reserved identifier: "
              + SoyFileNodeTransformer.RESERVED_IDENTIFIERS
              + ". Param: {0} is being skipped (no setters will be generated for this param). The"
              + " generated setter name was: {2}. To use this API, all parameter names for a given"
              + " template should be unique when converted to UpperCamelCase (with"
              + " non-alphanumeric characters stripped).");
  private static final SoyErrorKind FILE_NAME_COLLISION =
      SoyErrorKind.of(
          "While generating Soy Java invocation builders, multiple files in this soy fileset"
              + " mapped to the same file name: {0}. To use this api, soy file names should be"
              + " unique when converted to UpperCamelCase (with non-alpha-numeric characters"
              + " stripped).");
  private static final SoyErrorKind FUTURE_NAME_COLLISION =
      SoyErrorKind.of(
          "Achievement unlocked. You have a template with parameters named {0} and"
              + " {0}Future, preventing a future setter from being created for the first"
              + " parameter.");

  private final ErrorReporter errorReporter;
  private final String kytheCorpus;
  private final SoyFileNodeTransformer transformer;

  private IndentedLinesBuilder ilb; // Line formatter for the generated code.
  private KytheHelper kytheHelper;
  private ImmutableList.Builder<GeneratedFile> generatedFiles; // The generated Java files to write.

  public GenerateBuildersVisitor(
      ErrorReporter errorReporter,
      String javaPackage,
      String kytheCorpus,
      FileSetMetadata registry) {
    this.errorReporter = errorReporter;
    this.kytheCorpus = kytheCorpus;
    this.transformer = new SoyFileNodeTransformer(javaPackage, registry);
  }

  @Override
  public ImmutableList<GeneratedFile> exec(SoyNode node) {
    generatedFiles = new ImmutableList.Builder<>();
    ilb = null;
    kytheHelper = null;
    visit(node);

    ImmutableList<GeneratedFile> builtFileList = generatedFiles.build();
    logWarningIfFilenamesNotUnique(builtFileList);
    return builtFileList;
  }

  @Override
  protected void visitSoyFileSetNode(SoyFileSetNode node) {
    for (SoyFileNode soyFile : node.getChildren()) {
      visit(soyFile);
    }
  }

  @Override
  protected void visitSoyFileNode(SoyFileNode soyFile) {
    FileInfo fileInfo = transformer.transform(soyFile);
    ilb = new IndentedLinesBuilder(2);
    kytheHelper = new KytheHelper(kytheCorpus);
    appendFileHeaderAndImports(fileInfo);

    String javaClassNameForSoyFile = fileInfo.className();

    // Start of *FooTemplates class.
    appendJavadoc(
        ilb,
        "Wrapper class containing {@link com.google.template.soy.data.SoyTemplate} builders for"
            + " each template in: "
            + fileInfo.soyFileName()
            + ".",
        /* forceMultiline= */ false,
        /* wrapAt100Chars= */ true);
    ilb.appendLine(
        "@javax.annotation.Generated(\n"
            + "    value = \"com.google.template.soy.SoyParseInfoGenerator\""
            + (kytheHelper.isEnabled()
                ? ",\n    comments = \"kythe-inline-metadata:GeneratedCodeInfo\""
                : "")
            + ")");

    Span classSpan =
        kytheHelper
            .appendLineAndGetSpans(ilb, "public final class ", javaClassNameForSoyFile, " {")
            .get(1);
    kytheHelper.addKytheLinkTo(classSpan, soyFile);

    ilb.increaseIndent();

    appendProtoDescriptors(fileInfo, soyFile.getSoyTypeRegistry());
    HashMap<String, String> filePathToNamespace = new HashMap<>();
    fileInfo
        .fileNode()
        .getRequiredCssPaths()
        .forEach(
            cssPath -> {
              if (cssPath.getNamespace() != null
                  && !filePathToNamespace.containsKey(cssPath.resolvedPath().get())) {
                filePathToNamespace.put(
                    String.format("\"%s\"", cssPath.resolvedPath().get()),
                    String.format("\"%s\"", cssPath.getNamespace()));
              }
            });
    ilb.appendLine();
    appendJavadoc(
        ilb,
        "A map of filepath to symbol used for CSS resolution on server edit-refresh.",
        false,
        true);
    ilb.appendLineStart(
        "private static final com.google.common.collect.ImmutableMap<java.lang.String,"
            + " java.lang.String> "
            + CSS_MAP_FIELD
            + " = ");
    appendImmutableMap(ilb, "<java.lang.String, java.lang.String>", filePathToNamespace);
    ilb.appendLineEnd(";");
    ImmutableList<String> namespaces =
        Streams.concat(
                fileInfo.fileNode().getRequiredCssNamespaces().stream(),
                fileInfo.fileNode().getRequiredCssPaths().stream().map(p -> p.resolvedPath().get()),
                fileInfo.templates().stream()
                    .flatMap(
                        templateInfo ->
                            templateInfo.template().getRequiredCssNamespaces().stream()))
            .map(ns -> String.format("\"%s\"", ns))
            .collect(toImmutableList());
    ilb.appendLine();
    appendJavadoc(
        ilb, "A list of provided symbols used for css validation on edit refresh.", false, true);
    ilb.appendLineStart(
        "private static final com.google.common.collect.ImmutableList<java.lang.String> "
            + CSS_PROVIDES_FIELD
            + " = ");
    appendImmutableList(ilb, "<java.lang.String>", namespaces);
    ilb.appendLineEnd(";");
    // Add FooParams subclasses for the templates in this file.
    generateParamsClassesForEachTemplate(fileInfo);

    // End of *FooTemplates class.
    ilb.decreaseIndent();
    ilb.appendLine("}");

    // Add the file name and contents to the list of generated files to write.
    String fileName = javaClassNameForSoyFile + ".java";
    generatedFiles.add(
        GeneratedFile.create("", fileName, ilb.toString(), kytheHelper.getGeneratedCodeInfo()));
    ilb = null;
  }

  /** For each public, non-delegate template in the given soy file, generates a Foo inner class. */
  private void generateParamsClassesForEachTemplate(FileInfo soyFile) {
    soyFile
        .templates()
        .forEach(
            t -> {
              switch (t.status()) {
                case HANDLED:
                  visitTemplateInfo(t);
                  break;
                case NAME_COLLISION:
                  errorReporter.warn(
                      t.sourceLocation(), TEMPLATE_NAME_COLLISION, t.templateName(), t.className());
                  break;
              }
            });
  }

  /**
   * Writes a Foo subclass for the given template. The class extends {@link
   * com.google.template.soy.data.BaseSoyTemplateImpl}, which implements {@link
   * com.google.template.soy.data.SoyTemplate}.
   */
  private void visitTemplateInfo(TemplateInfo template) {
    String templateClass = template.className();

    // Start of Foo class.
    String templateDescription = template.soyDocDesc();
    ilb.appendLine();
    appendJavadoc(
        ilb,
        "Template params for "
            + template.templateNameForUserMsgs()
            + (templateDescription != null ? ": " + templateDescription : "."),
        /* forceMultiline= */ false,
        /* wrapAt100Chars= */ true);

    Span classNameSpan =
        kytheHelper
            .appendLineAndGetSpans(
                ilb,
                "public static final class ",
                templateClass,
                " extends com.google.template.soy.data.BaseSoyTemplateImpl {")
            .get(1);
    kytheHelper.addKytheLinkTo(classNameSpan, template);

    ilb.increaseIndent();
    ilb.appendLine();
    ilb.appendLine(
        "private static final java.lang.String "
            + TEMPLATE_NAME_FIELD
            + " = \""
            + template.templateName()
            + "\";");
    ilb.appendLine();

    appendFutureWrapperMethod(templateClass);

    // Constructor for Foo.
    ilb.appendLine(
        "private "
            + templateClass
            + "(com.google.common.collect.ImmutableMap<java.lang.String,"
            + " com.google.template.soy.data.SoyValueProvider> data) {");
    ilb.increaseIndent();
    ilb.appendLine("super(data);");
    ilb.decreaseIndent();
    ilb.appendLine("}");

    ilb.appendLine();

    ilb.appendLine("@java.lang.Override");
    ilb.appendLine("public final java.lang.String getTemplateName() {");
    ilb.increaseIndent();
    ilb.appendLine("return " + TEMPLATE_NAME_FIELD + ";");
    ilb.decreaseIndent();
    ilb.appendLine("}");
    ilb.appendLine();

    appendParamsBuilderClass(template, templateClass);

    // End of Foo class.
    ilb.decreaseIndent();
    ilb.appendLine("}");
    ilb.appendLine();
  }

  private void appendProtoDescriptors(FileInfo fileInfo, SoyTypeRegistry typeRegistry) {
    List<String> protoTypes =
        fileInfo.getProtoTypes(typeRegistry).stream().sorted().collect(toList());

    if (protoTypes.isEmpty()) {
      return;
    }

    ilb.appendLine();
    appendJavadoc(
        ilb,
        "The list of protos used by all templates (public and private) in this Soy file, which are "
            + "used by 1) the edit-refresh development compiler and 2) the java compiler to "
            + "enforce strict proto deps.",
        false,
        true);
    ilb.appendLineStart(
        "private static final com.google.common.collect.ImmutableList<"
            + "com.google.protobuf.Descriptors.FileDescriptor> "
            + PROTOS_FIELD
            + " = ");
    appendFunctionCallWithParamsOnNewLines(
        ilb, "com.google.common.collect.ImmutableList.of", protoTypes);
    ilb.appendLineEnd(";");
  }

  /**
   * Adds a static method to each Params class: {@code public static SoyTemplate.AsyncWrapper<Foo>
   * wrapFuture(ListenableFuture<Foo>)}. This utility is needed for supporting Producers + some Apps
   * Framework utility classes.
   *
   * @see com.google.apps.framework.template.StructuredPageResponse
   */
  private void appendFutureWrapperMethod(String paramsClass) {
    appendJavadoc(
        ilb,
        "Wraps a ListenableFuture<"
            + paramsClass
            + "> as a SoyTemplate.AsyncWrapper<"
            + paramsClass
            + ">",
        false,
        true);
    ilb.appendLine(
        "public static com.google.template.soy.data.SoyTemplate.AsyncWrapper<"
            + paramsClass
            + "> wrapFuture(com.google.common.util.concurrent.ListenableFuture<"
            + paramsClass
            + "> paramsFuture) {");
    ilb.increaseIndent();
    ilb.appendLine(
        "return new com.google.template.soy.data.SoyTemplate.AsyncWrapper<>("
            + TEMPLATE_NAME_FIELD
            + ", paramsFuture);");
    ilb.decreaseIndent();
    ilb.appendLine("}");
    ilb.appendLine();
  }

  /**
   * Appends a builder class for template "foo" with parameter setting methods. This class extends
   * the {@link com.google.template.soy.data.BaseSoyTemplateImpl.AbstractBuilder} class.
   */
  private void appendParamsBuilderClass(TemplateInfo template, String templateParamsClassname) {
    appendJavadoc(ilb, "Creates a new Builder instance.", false, true);
    ilb.appendLine("public static Builder builder() {");
    ilb.increaseIndent();
    ilb.appendLine("return new Builder();");
    ilb.decreaseIndent();
    ilb.appendLine("}");
    ilb.appendLine();

    // Will contain handled and unhandled params. We include some types of unhandled params so that
    // they still end up in the generated list of params.
    List<ParamInfo> combinedParams =
        template.params().stream()
            .filter(
                info -> {
                  switch (info.status()) {
                    case HANDLED:
                    case UNHANDLED_TYPE:
                      return true;
                    case NAME_COLLISION:
                      errorReporter.warn(
                          info.sourceLocation(),
                          PARAM_NAME_COLLISION,
                          info.name(),
                          template.templateName(),
                          info.setterName());
                      return true;
                    case JAVA_INCOMPATIBLE:
                      break;
                    case INDIRECT_INCOMPATIBLE_TYPES:
                      errorReporter.warn(
                          info.sourceLocation(),
                          TYPE_COLLISION,
                          info.name(),
                          template.templateName());
                      break;
                    case INDIRECT_PROTO:
                      errorReporter.warn(
                          info.sourceLocation(),
                          INDIRECT_PROTO,
                          info.name(),
                          template.templateName());
                      break;
                  }
                  return false;
                })
            .collect(toList());
    List<ParamInfo> nonInjectedParams =
        combinedParams.stream().filter(p -> !p.injected()).collect(toList());

    if (nonInjectedParams.stream().noneMatch(ParamInfo::requiredAndNotIndirect)) {
      // Invoke the constructor directly. For these templates it could allow callers to avoid
      // loading the builder completely.
      ilb.appendLine(
          "private static final "
              + templateParamsClassname
              + " "
              + DEFAULT_INSTANCE_FIELD
              + " = new "
              + templateParamsClassname
              + "(com.google.common.collect.ImmutableMap.of());");
      ilb.appendLine();

      appendJavadoc(
          ilb,
          "Creates a new instance of "
              + templateParamsClassname
              + " with no parameters set. This method was generated because all template"
              + " parameters are optional.",
          false,
          true);
      ilb.appendLine("public static " + templateParamsClassname + " getDefaultInstance() {");
      ilb.increaseIndent();
      ilb.appendLine("return " + DEFAULT_INSTANCE_FIELD + ";");
      ilb.decreaseIndent();
      ilb.appendLine("}");
      ilb.appendLine();
    }
    appendParamConstants(ilb, combinedParams);

    boolean anyAccumulatorParameters =
        nonInjectedParams.stream()
            .flatMap(param -> param.javaTypes().stream())
            .anyMatch(
                javaType ->
                    javaType instanceof RecordJavaType && ((RecordJavaType) javaType).isList());
    // Start of Foo.Builder class.
    ilb.appendLine(
        "public static final class Builder extends"
            + " com.google.template.soy.data.BaseSoyTemplateImpl."
            + (anyAccumulatorParameters
                ? "AbstractBuilderWithAccumulatorParameters"
                : "AbstractBuilder")
            + "<Builder, "
            + templateParamsClassname
            + "> {");
    ilb.appendLine();
    ilb.increaseIndent();

    // Constructor for Foo.Builder.
    ilb.appendLine("private Builder() {");
    ilb.increaseIndent();
    ilb.appendLine("super(", nonInjectedParams.size(), ");");
    appendRecordListInitializations(ilb, nonInjectedParams);
    ilb.decreaseIndent();
    ilb.appendLine("}");
    ilb.appendLine();

    // #allParams() for FooTemplate.Builder.
    ilb.appendLine("@java.lang.Override");
    ilb.appendLine(
        "protected"
            + " com.google.common.collect.ImmutableSet<com.google.template.soy.data.SoyTemplateParam<?>>"
            + " allParams() {");
    ilb.increaseIndent();
    ilb.appendLine("return " + PARAMS_FIELD + ";");
    ilb.decreaseIndent();
    ilb.appendLine("}");
    ilb.appendLine();

    // #buildInternal() for FooTemplate.Builder.
    ilb.appendLine("@java.lang.Override");
    ilb.appendLine(
        "protected "
            + templateParamsClassname
            + " buildInternal(com.google.common.collect.ImmutableMap<java.lang.String,"
            + " com.google.template.soy.data.SoyValueProvider> data) {");
    ilb.increaseIndent();
    ilb.appendLine("return new " + templateParamsClassname + "(data);");
    ilb.decreaseIndent();
    ilb.appendLine("}");

    // Add setters for each direct template param.
    nonInjectedParams.stream()
        .filter(p -> p.status() == ParamStatus.HANDLED)
        .forEach(p -> writeSettersForParam(p, template));

    ilb.appendLine();

    // End of FooTemplateInvocation.Builder class.
    ilb.decreaseIndent();
    ilb.appendLine("}");
  }

  private static void appendParamConstants(IndentedLinesBuilder ilb, List<ParamInfo> params) {
    Set<String> usedNames = new HashSet<>();
    List<String> nonInjected = new ArrayList<>();
    for (ParamInfo param : params) {
      while (usedNames.contains(param.constantFieldName())) {
        param.updateConstantFieldName();
      }
      String fieldName = param.constantFieldName();
      usedNames.add(fieldName);
      if (!param.injected()) {
        nonInjected.add(fieldName);
      }

      String genericType = "?";
      List<JavaType> types = param.javaTypes();
      if (types.size() == 1) {
        JavaType javaType = types.get(0);
        // this is basically 'instanceof RecordJavaType' at this point
        if (javaType.isTypeLiteralSupported()) {
          genericType = javaType.asTypeLiteralString();
        }
      }

      // Make any param that supports type literal public so it can be used with
      // TemplateParamModule, SoyTemplateData, AbstractBuilder, and tests. Union types, records, and
      // CSS params will be private since they can't be represented as a single specific type
      // literal.
      String visibility = !"?".equals(genericType) ? "public" : "private";

      // These values correspond to static factory methods on SoyTemplateParam.
      CodeGenUtils.Member factory = STANDARD_P;
      if (param.injected()) {
        factory = INJECTED_P;
      } else if (param.indirect()) {
        factory = INDIRECT_P;
      }

      String paramDescription = param.param().getDescription();
      if (paramDescription == null) {
        paramDescription = "";
      } else {
        paramDescription += " ";
      }

      String typeToken =
          "?".equals(genericType)
              // TODO(jcg): this should probably be a wildcard type
              ? "com.google.common.reflect.TypeToken.of(java.lang.Object.class)"
              : (genericType.matches("(\\.|\\w)+")
                  ? "com.google.common.reflect.TypeToken.of(" + genericType + ".class" + ")"
                  : "new com.google.common.reflect.TypeToken<" + genericType + ">() {}");
      ilb.appendLine(
          String.format(
              "/** {@%s %s} %s*/",
              param.injected() ? "inject" : "param", param.name(), paramDescription));
      ilb.appendLine(
          String.format(
              "%s static final com.google.template.soy.data.SoyTemplateParam<%s>",
              visibility, genericType));
      ilb.increaseIndent(2);
      ilb.appendLine(fieldName, " =");
      ilb.increaseIndent(2);
      ilb.appendLine(factory, "(");
      ilb.increaseIndent(2);
      ilb.appendLine("\"", param.name(), "\",");
      ilb.appendLine("/* required= */ ", param.required(), ",");
      ilb.appendLine(typeToken, ");");
      ilb.decreaseIndent(6);
      ilb.appendLine();
    }

    ilb.appendLineStart(
        "private static final"
            + " com.google.common.collect.ImmutableSet<com.google.template.soy.data.SoyTemplateParam<?>>"
            + " "
            + PARAMS_FIELD
            + " = ");
    // Omit injected params from the list of params passed to the builder.
    appendFunctionCallWithParamsOnNewLines(
        ilb, "com.google.common.collect.ImmutableSet.of", nonInjected);
    ilb.appendLineEnd(";");
    ilb.appendLine();
  }

  private static void appendRecordListInitializations(
      IndentedLinesBuilder ilb, List<ParamInfo> params) {
    // For every required param that's of type list<[...]> (list of records), initialize the list
    // so that upon building the template we do not throw an error for zero records.
    for (ParamInfo param : params) {
      if (param.required()) {
        List<JavaType> types = param.javaTypes();
        if (types.size() == 1
            && types.get(0) instanceof RecordJavaType
            && ((RecordJavaType) types.get(0)).isList()) {
          ilb.appendLine(String.format("%s(%s);", INIT_LIST_PARAM, param.constantFieldName()));
        }
      }
    }
  }

  /** Appends the file header and imports for the generated *FooTemplates.java */
  private void appendFileHeaderAndImports(FileInfo soyFile) {
    // Header.
    ilb.appendLine("// This file was automatically generated by the Soy compiler.");
    ilb.appendLine("// Please don't edit this file by hand.");
    ilb.appendLine("// source: " + soyFile.soyFilePath().path()); // For Code Search link.
    ilb.appendLine();
    ilb.appendLine("package " + soyFile.packageName() + ";");
    ilb.appendLine();
    ilb.appendLine();

    // No Imports!
    // It is annoying and verbose but by fully qualifying all type names we can avoid conflicts
    // with user defined symbols
  }

  /**
   * Writes setter methods each of the java types that this param can be (e.g union int | string
   * would generate setFoo(int) and setFoo(string)).
   *
   * <p>TODO(b/77550695): Update docs for how we handle futures.
   */
  private void writeSettersForParam(ParamInfo param, TemplateInfo template) {
    // Add setters for this param.
    param.javaTypes().forEach(javaType -> writeSetter(param, template, javaType));

    // For now only write the future interface if the setter is not already overloaded
    switch (param.futureStatus()) {
      case HANDLED:
        for (JavaType futureType : param.futureTypes()) {
          writeFutureSetter(ilb, param, template, new FutureJavaType(futureType));
        }
        break;
      case NAME_COLLISION:
        errorReporter.warn(param.sourceLocation(), FUTURE_NAME_COLLISION, param.name());
        break;
      case UNHANDLED:
        break;
    }
  }

  /** Writes a setter method for the given param and java type. */
  private void writeSetter(ParamInfo param, TemplateInfo template, JavaType javaType) {
    String paramDescription = param.param().getDescription();
    ilb.appendLine();
    appendJavadoc(
        ilb,
        "Sets "
            + param.name()
            + (Strings.isNullOrEmpty(paramDescription) ? "." : ": " + paramDescription),
        /* forceMultiline= */ false,
        /* wrapAt100Chars= */ true);

    if (javaType instanceof RecordJavaType) {
      writeRecordSetter(param, template, (RecordJavaType) javaType);
    } else {
      String javaTypeString = javaType.toJavaTypeString();
      boolean nullable = javaType.isNullable();

      ilb.appendLine("@com.google.errorprone.annotations.CanIgnoreReturnValue");
      Span paramSpan =
          kytheHelper
              .appendLineAndGetSpans(
                  ilb,
                  "public Builder ",
                  param.setterName(),
                  "("
                      + (nullable ? "@javax.annotation.Nullable " : "")
                      + javaTypeString
                      + " value) {")
              .get(1);
      kytheHelper.addKytheLinkTo(paramSpan, param, template);
      ilb.increaseIndent();

      String newVariableName = javaType.asInlineCast("value");
      ilb.appendLine(
          "return " + SET_PARAM_INTERNAL + "(",
          param.constantFieldName(),
          ", ",
          newVariableName,
          ");");
      ilb.decreaseIndent();
      ilb.appendLine("}");
    }
  }

  private void writeRecordSetter(ParamInfo param, TemplateInfo template, RecordJavaType type) {
    ilb.appendLine("@com.google.errorprone.annotations.CanIgnoreReturnValue");

    Span paramSpan =
        kytheHelper
            .appendLineStartAndGetSpans(
                ilb, "public Builder ", type.isList() ? param.adderName() : param.setterName(), "(")
            .get(1);
    kytheHelper.addKytheLinkTo(paramSpan, param, template);

    List<String> paramNames = type.getJavaTypeMap().keySet().asList();
    List<String> javaParamNames = new ArrayList<>();

    boolean first = true;
    for (Map.Entry<String, JavaType> entry : type.getJavaTypeMap().entrySet()) {
      String paramName = makeParamName(entry.getKey());
      javaParamNames.add(paramName);

      if (!first) {
        ilb.appendLineMiddle(", ");
      }
      JavaType paramType = entry.getValue();
      if (paramType.isNullable()) {
        ilb.appendLineMiddle("@javax.annotation.Nullable ");
      }
      ilb.appendLineMiddle(paramType.toJavaTypeString(), " ", paramName);
      first = false;
    }
    ilb.appendLineEnd(") {");
    ilb.increaseIndent();

    CodeGenUtils.Member delegate = type.isList() ? ADD_TO_LIST_PARAM : SET_PARAM_INTERNAL;

    ilb.appendLineStart(
        "return ", delegate, "(", param.constantFieldName(), ", " + AS_RECORD + "(");
    int numParams = paramNames.size();
    for (int i = 0; i < numParams; i++) {
      if (i != 0) {
        ilb.appendLineMiddle(", ");
      }
      ilb.appendLineMiddle(
          "\"",
          paramNames.get(i),
          "\", ",
          type.getJavaTypeMap().get(paramNames.get(i)).asInlineCast(javaParamNames.get(i)));
    }
    ilb.appendLineEnd("));");
    ilb.decreaseIndent();
    ilb.appendLine("}");
  }

  /** Writes a setter method for the given param and java type. */
  private void writeFutureSetter(
      IndentedLinesBuilder ilb, ParamInfo param, TemplateInfo template, FutureJavaType javaType) {

    ilb.appendLine();
    appendJavadoc(
        ilb,
        "Future compatible version of {@link #"
            + param.setterName()
            + "("
            + stripGenerics(javaType.getType().toJavaTypeString())
            + ")}.",
        /* forceMultiline= */ false,
        /* wrapAt100Chars= */ true);
    ilb.appendLine("@com.google.errorprone.annotations.CanIgnoreReturnValue");

    Span paramSpan =
        kytheHelper
            .appendLineAndGetSpans(
                ilb,
                "public Builder ",
                param.futureSetterName(),
                "(" + javaType.toJavaTypeString() + " future) {")
            .get(1);
    kytheHelper.addKytheLinkTo(paramSpan, param, template);

    ilb.increaseIndent();

    ilb.appendLine(
        "return "
            + SET_PARAM_INTERNAL
            + "("
            + param.constantFieldName()
            + ", "
            + javaType.asInlineCast("future")
            + ");");
    ilb.decreaseIndent();
    ilb.appendLine("}");
  }

  private static String stripGenerics(String type) {
    String newType = type;
    do {
      type = newType;
      newType = type.replaceAll("<[^>]*>", "");
    } while (!newType.equals(type));
    return newType;
  }

  /** Logs a warning if two soy files mapped to the same generated java file name. */
  private void logWarningIfFilenamesNotUnique(ImmutableList<GeneratedFile> files) {
    ImmutableList<String> duplicateFilenames =
        files.stream().collect(groupingBy(GeneratedFile::fileName, counting())).entrySet().stream()
            .filter(e -> e.getValue() > 1) // We only care about duplicate filenames.
            .map(Map.Entry::getKey)
            .collect(toImmutableList());

    for (String fileName : duplicateFilenames) {
      errorReporter.warn(UNKNOWN, FILE_NAME_COLLISION, fileName);
    }
  }

  private static String makeParamName(String s) {
    s = makeLowerCamelCase(s);
    return isReservedKeyword(s) ? s + "_" : s;
  }
}

redpajama_set_name: RedPajamaGithub


text:
<?php

namespace Lks\ProjectManagementBundle;

use Symfony\Component\HttpKernel\Bundle\Bundle;

class LksProjectManagementBundle extends Bundle
{
}


text:
<?php

class AW_Rma_Block_Adminhtml_Rma_Edit extends Mage_Adminhtml_Block_Widget_Form_Container
{

    public function __construct()
    {
        $this->_controller = 'adminhtml_rma';
        parent::__construct();

        $this->_objectId = 'id';
        $this->_blockGroup = 'awrma';

        $this->_updateButton('save', 'onclick', 'formControl.validateForm()');
        $this->_updateButton('save', 'id', 'awrma-save-button');

        $this->_removeButton('delete');

        $this->_addButton(
            'saveandcontinueedit',
            array(
                'label'   => $this->__('Save And Continue Edit'),
                'onclick' => 'awrmaSaveAndContinueEdit()',
                'class'   => 'save',
                'id'      => 'awrma-save-and-continue'
            ),
            -200
        );

        $rmaRequest = Mage::registry('awrmaformdatarma');
        if (Mage::helper('awrma/config')->getAllowPrintLabel($rmaRequest->getStoreId()))
            $this->_addButton(
                'printlabel',
                array(
                    'label' => $this->__('Print'),
                    'onclick' => "saveAndPrint()",
                    'class' => 'scalable save',
                    'id' => 'awrma-print'
                ),
                -300
            );

        $this->_formScripts[] =
                "var formControl = new AWRMAAdminRmaFormControl(this, editForm);
formControl.observeItemsCount();

function awrmaSaveAndContinueEdit() {
    if($('edit_form').action.indexOf('continue/1/')<0)
        $('edit_form').action += 'continue/1/';

    formControl.validateForm();
}

function saveAndPrint() {
    if($('edit_form').action.indexOf('print/1/')<0)
        $('edit_form').action += 'print/1/';

    formControl.validateForm();
}";
        if ($this->getRequest()->getParam('printstore') && $this->getRequest()->getParam('printurl')) {
            $this->_formScripts[] =
                    "function openInNewWindow(href) {
    var newWindow = window.open(href, '_blank');
    newWindow.focus();
    return false;
}
openInNewWindow('" . Mage::app()->getStore($this->getRequest()->getParam('printstore'))
                        ->getUrl(
                            'awrma/guest_rma/printform', array('id' => $this->getRequest()->getParam('printurl'))
                        )
                    . "');";
        }
    }

    public function getHeaderText()
    {
        $_rma = Mage::registry('awrmaformdatarma');
        if ($_rma->getId()) {
            return $this->__('RMA ' . $_rma->getTextId() . ' – ' . $_rma->getStatusName());
        } else {
            return $this->__('New RMA');
        }
    }
}


text:
<configuration>

    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <!-- Colored Highlighted log -->
        <encoder>
            <pattern>
                %d{yyyy-MM-dd HH:mm:ss.SSS} %highlight(%-5level) [%-40.40logger{10}] - %msg%n
            </pattern>
        </encoder>
    </appender>

    <root level="INFO">
        <appender-ref ref="STDOUT" />
    </root>
</configuration>


text:
#include <folly/io/async/test/AsyncSSLSocketTest.h>

#include <gtest/gtest.h>
#include <pthread.h>

#include <folly/io/async/AsyncSSLSocket.h>
#include <folly/io/async/EventBase.h>
#include <folly/io/async/SSLContext.h>

using std::string;
using std::vector;
using std::min;
using std::cerr;
using std::endl;
using std::list;

namespace folly {

class AttachDetachClient : public AsyncSocket::ConnectCallback,
                           public AsyncTransportWrapper::WriteCallback,
                           public AsyncTransportWrapper::ReadCallback {
 private:
  EventBase *eventBase_;
  std::shared_ptr<AsyncSSLSocket> sslSocket_;
  std::shared_ptr<SSLContext> ctx_;
  folly::SocketAddress address_;
  char buf_[128];
  char readbuf_[128];
  uint32_t bytesRead_;
 public:
  AttachDetachClient(EventBase *eventBase, const folly::SocketAddress& address)
      : eventBase_(eventBase), address_(address), bytesRead_(0) {
    ctx_.reset(new SSLContext());
    ctx_->setOptions(SSL_OP_NO_TICKET);
    ctx_->ciphers("ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
  }

  void connect() {
    sslSocket_ = AsyncSSLSocket::newSocket(ctx_, eventBase_);
    sslSocket_->connect(this, address_);
  }

  void connectSuccess() noexcept override {
    cerr << "client SSL socket connected" << endl;

    for (int i = 0; i < 1000; ++i) {
      sslSocket_->detachSSLContext();
      sslSocket_->attachSSLContext(ctx_);
    }

    EXPECT_EQ(ctx_->getSSLCtx()->references, 2);

    sslSocket_->write(this, buf_, sizeof(buf_));
    sslSocket_->setReadCB(this);
    memset(readbuf_, 'b', sizeof(readbuf_));
    bytesRead_ = 0;
  }

  void connectErr(const AsyncSocketException& ex) noexcept override
  {
    cerr << "AttachDetachClient::connectError: " << ex.what() << endl;
    sslSocket_.reset();
  }

  void writeSuccess() noexcept override {
    cerr << "client write success" << endl;
  }

  void writeErr(size_t /* bytesWritten */,
                const AsyncSocketException& ex) noexcept override {
    cerr << "client writeError: " << ex.what() << endl;
  }

  void getReadBuffer(void** bufReturn, size_t* lenReturn) override {
    *bufReturn = readbuf_ + bytesRead_;
    *lenReturn = sizeof(readbuf_) - bytesRead_;
  }
  void readEOF() noexcept override {
    cerr << "client readEOF" << endl;
  }

  void readErr(const AsyncSocketException& ex) noexcept override {
    cerr << "client readError: " << ex.what() << endl;
  }

  void readDataAvailable(size_t len) noexcept override {
    cerr << "client read data: " << len << endl;
    bytesRead_ += len;
    if (len == sizeof(buf_)) {
      EXPECT_EQ(memcmp(buf_, readbuf_, bytesRead_), 0);
      sslSocket_->closeNow();
    }
  }
};

/**
 * Test passing contexts between threads
 */
TEST(AsyncSSLSocketTest2, AttachDetachSSLContext) {
  // Start listening on a local port
  WriteCallbackBase writeCallback;
  ReadCallback readCallback(&writeCallback);
  HandshakeCallback handshakeCallback(&readCallback);
  SSLServerAcceptCallbackDelay acceptCallback(&handshakeCallback);
  TestSSLServer server(&acceptCallback);

  EventBase eventBase;
  EventBaseAborter eba(&eventBase, 3000);
  std::shared_ptr<AttachDetachClient> client(
    new AttachDetachClient(&eventBase, server.getAddress()));

  client->connect();
  eventBase.loop();
}

}  // folly

int main(int argc, char *argv[]) {
  signal(SIGPIPE, SIG_IGN);
  folly::SSLContext::setSSLLockTypes({
      {CRYPTO_LOCK_EVP_PKEY, folly::SSLContext::LOCK_NONE},
      {CRYPTO_LOCK_SSL_SESSION, folly::SSLContext::LOCK_SPINLOCK},
      {CRYPTO_LOCK_SSL_CTX, folly::SSLContext::LOCK_NONE}});
  testing::InitGoogleTest(&argc, argv);
  gflags::ParseCommandLineFlags(&argc, &argv, true);
  return RUN_ALL_TESTS();
}


text:
// Copyright 2022 Google LLC
//
// 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 com.google.api.ads.admanager.jaxws.v202205;

import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlType;

/**
 *
Java class for UserDomainTargetingError.Reason.
 *
 *
The following schema fragment specifies the expected content contained within this class.
 *

 *

 * <simpleType name="UserDomainTargetingError.Reason">
 *   <restriction base="{http://www.w3.org/2001/XMLSchema}string">
 *     <enumeration value="INVALID_DOMAIN_NAMES"/>
 *     <enumeration value="UNKNOWN"/>
 *   </restriction>
 * </simpleType>
 *

 *
 */
@XmlType(name = "UserDomainTargetingError.Reason")
@XmlEnum
public enum UserDomainTargetingErrorReason {

    /**
     *
     *                 Invalid domain names. Domain names must be at most 67 characters long.
     *                 And must contain only alphanumeric characters and hyphens.
     *
     *
     */
    INVALID_DOMAIN_NAMES,

    /**
     *
     *                 The value returned if the actual value is not exposed by the requested API version.
     *
     *
     */
    UNKNOWN;

    public String value() {
        return name();
    }

    public static UserDomainTargetingErrorReason fromValue(String v) {
        return valueOf(v);
    }

}


text:
from shopify import ApiAccess, ApiAccessError
from test.test_helper import TestCase

class ApiAccessTest(TestCase):
    def test_creating_scopes_from_a_string_works_with_a_comma_separated_list(self):
        deserialized_read_products_write_orders = ApiAccess("read_products,write_orders")
        serialized_read_products_write_orders = str(deserialized_read_products_write_orders)
        expected_read_products_write_orders = ApiAccess(["read_products", "write_orders"])

        self.assertEqual(expected_read_products_write_orders, ApiAccess(serialized_read_products_write_orders))

    def test_creating_api_access_from_invalid_scopes_raises(self):
        with self.assertRaises(ApiAccessError) as cm:
            api_access = ApiAccess("bad_scope, read_orders,write_orders")

        self.assertEqual("'bad_scope' is not a valid access scope", str(cm.exception))

    def test_returns_list_of_reduced_scopes(self):
        api_access = ApiAccess("read_products, read_orders,write_orders")
        expected_scopes = set(["read_products", "write_orders"])
        scopes = list(api_access)

        self.assertEqual(expected_scopes, set(scopes))

    def test_write_is_the_same_access_as_read_write_on_the_same_resource(self):
        read_write_orders = ApiAccess(["read_orders", "write_orders"])
        write_orders = ApiAccess("write_orders")

        self.assertEqual(write_orders, read_write_orders)

    def test_write_is_the_same_access_as_read_write_on_the_same_unauthenticated_resource(self):
        unauthenticated_read_write_orders = ApiAccess(["unauthenticated_read_orders", "unauthenticated_write_orders"])
        unauthenticated_write_orders = ApiAccess("unauthenticated_write_orders")

        self.assertEqual(unauthenticated_write_orders, unauthenticated_read_write_orders)

    def test_read_is_not_the_same_as_read_write_on_the_same_resource(self):
        read_orders = ApiAccess("read_orders")
        read_write_orders = ApiAccess(["write_orders", "read_orders"])

        self.assertNotEqual(read_write_orders, read_orders)

    def test_two_different_resources_are_not_equal(self):
        read_orders = ApiAccess("read_orders")
        read_products = ApiAccess("read_products")

        self.assertNotEqual(read_orders, read_products)

    def test_two_identical_scopes_are_equal(self):
        read_orders = ApiAccess("read_orders")
        read_orders_identical = ApiAccess("read_orders")

        self.assertEqual(read_orders, read_orders_identical)

    def test_unauthenticated_is_not_implied_by_authenticated_access(self):
        unauthenticated_orders = ApiAccess("unauthenticated_read_orders")
        authenticated_read_orders = ApiAccess("read_orders")
        authenticated_write_orders = ApiAccess("write_orders")

        self.assertNotEqual(unauthenticated_orders, authenticated_read_orders)
        self.assertNotEqual(unauthenticated_orders, authenticated_write_orders)

    def test_scopes_covers_is_truthy_for_same_scopes(self):
        read_orders = ApiAccess("read_orders")
        read_orders_identical = ApiAccess("read_orders")

        self.assertTrue(read_orders.covers(read_orders_identical))

    def test_covers_is_falsy_for_different_scopes(self):
        read_orders = ApiAccess("read_orders")
        read_products = ApiAccess("read_products")

        self.assertFalse(read_orders.covers(read_products))

    def test_covers_is_truthy_for_read_when_the_set_has_read_write(self):
        write_products = ApiAccess("write_products")
        read_products = ApiAccess("read_products")

        self.assertTrue(write_products.covers(read_products))

    def test_covers_is_truthy_for_read_when_the_set_has_read_write_for_that_resource_and_others(self):
        write_products_and_orders = ApiAccess(["write_products", "write_orders"])
        read_orders = ApiAccess("read_orders")

        self.assertTrue(write_products_and_orders.covers(read_orders))

    def test_covers_is_truthy_for_write_when_the_set_has_read_write_for_that_resource_and_others(self):
        write_products_and_orders = ApiAccess(["write_products", "write_orders"])
        write_orders = ApiAccess("write_orders")

        self.assertTrue(write_products_and_orders.covers(write_orders))

    def test_covers_is_truthy_for_subset_of_scopes(self):
        write_products_orders_customers = ApiAccess(["write_products", "write_orders", "write_customers"])
        write_orders_products = ApiAccess(["write_orders", "read_products"])

        self.assertTrue(write_products_orders_customers.covers(write_orders_products))

    def test_covers_is_falsy_for_sets_of_scopes_that_have_no_common_elements(self):
        write_products_orders_customers = ApiAccess(["write_products", "write_orders", "write_customers"])
        write_images_read_content = ApiAccess(["write_images", "read_content"])

        self.assertFalse(write_products_orders_customers.covers(write_images_read_content))

    def test_covers_is_falsy_for_sets_of_scopes_that_have_only_some_common_access(self):
        write_products_orders_customers = ApiAccess(["write_products", "write_orders", "write_customers"])
        write_products_read_content = ApiAccess(["write_products", "read_content"])

        self.assertFalse(write_products_orders_customers.covers(write_products_read_content))

    def test_duplicate_scopes_resolve_to_one_scope(self):
        read_orders_duplicated = ApiAccess(["read_orders", "read_orders", "read_orders", "read_orders"])
        read_orders = ApiAccess("read_orders")

        self.assertEqual(read_orders, read_orders_duplicated)

    def test_to_s_outputs_scopes_as_a_comma_separated_list_without_implied_read_scopes(self):
        serialized_read_products_write_orders = "read_products,write_orders"
        read_products_write_orders = ApiAccess(["read_products", "read_orders", "write_orders"])

        self.assertIn("read_products", str(read_products_write_orders))
        self.assertIn("write_orders", str(read_products_write_orders))

    def test_to_a_outputs_scopes_as_an_array_of_strings_without_implied_read_scopes(self):
        serialized_read_products_write_orders = ["write_orders", "read_products"]
        read_products_write_orders = ApiAccess(["read_products", "read_orders", "write_orders"])

        self.assertEqual(set(serialized_read_products_write_orders), set(list(read_products_write_orders)))

    def test_creating_scopes_removes_extra_whitespace_from_scope_name_and_blank_scope_names(self):
        deserialized_read_products_write_orders = ApiAccess([" read_products", "  ", "write_orders "])
        serialized_read_products_write_orders = str(deserialized_read_products_write_orders)
        expected_read_products_write_orders = ApiAccess(["read_products", "write_orders"])

        self.assertEqual(expected_read_products_write_orders, ApiAccess(serialized_read_products_write_orders))

    def test_creating_scopes_from_a_string_works_with_a_comma_separated_list(self):
        deserialized_read_products_write_orders = ApiAccess("read_products,write_orders")
        serialized_read_products_write_orders = str(deserialized_read_products_write_orders)
        expected_read_products_write_orders = ApiAccess(["read_products", "write_orders"])

        self.assertEqual(expected_read_products_write_orders, ApiAccess(serialized_read_products_write_orders))

    def test_using_to_s_from_one_scopes_to_construct_another_will_be_equal(self):
        read_products_write_orders = ApiAccess(["read_products", "write_orders"])

        self.assertEqual(read_products_write_orders, ApiAccess(str(read_products_write_orders)))

    def test_using_to_a_from_one_scopes_to_construct_another_will_be_equal(self):
        read_products_write_orders = ApiAccess(["read_products", "write_orders"])

        self.assertEqual(read_products_write_orders, ApiAccess(list(read_products_write_orders)))