text:
package com.google.errorprone.bugpatterns;

import static com.google.errorprone.BugPattern.Category.JUNIT;
import static com.google.errorprone.BugPattern.MaturityLevel.EXPERIMENTAL;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;
import static com.google.errorprone.matchers.JUnitMatchers.JUNIT_AFTER_ANNOTATION;
import static com.google.errorprone.matchers.JUnitMatchers.JUNIT_BEFORE_ANNOTATION;
import static com.google.errorprone.matchers.Matchers.assertStatement;
import static com.google.errorprone.matchers.Matchers.assignment;
import static com.google.errorprone.matchers.Matchers.booleanConstant;
import static com.google.errorprone.matchers.Matchers.booleanLiteral;
import static com.google.errorprone.matchers.Matchers.continueStatement;
import static com.google.errorprone.matchers.Matchers.ignoreParens;
import static com.google.errorprone.matchers.Matchers.isInstanceField;
import static com.google.errorprone.matchers.Matchers.isSameType;
import static com.google.errorprone.matchers.Matchers.isSubtypeOf;
import static com.google.errorprone.matchers.Matchers.isVariable;
import static com.google.errorprone.matchers.Matchers.methodInvocation;
import static com.google.errorprone.matchers.Matchers.nextStatement;
import static com.google.errorprone.matchers.Matchers.returnStatement;
import static com.google.errorprone.matchers.Matchers.throwStatement;
import static com.google.errorprone.matchers.Matchers.toType;
import static com.google.errorprone.matchers.method.MethodMatchers.anyMethod;
import static com.google.errorprone.matchers.method.MethodMatchers.staticMethod;

import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState;
import com.google.errorprone.bugpatterns.BugChecker.TryTreeMatcher;
import com.google.errorprone.fixes.SuggestedFix;
import com.google.errorprone.matchers.ChildMultiMatcher;
import com.google.errorprone.matchers.ChildMultiMatcher.MatchType;
import com.google.errorprone.matchers.Description;
import com.google.errorprone.matchers.JUnitMatchers;
import com.google.errorprone.matchers.Matcher;
import com.google.errorprone.matchers.Matchers;
import com.google.errorprone.matchers.MultiMatcher;
import com.google.errorprone.matchers.NextStatement;
import com.google.errorprone.util.ASTHelpers;

import com.sun.source.tree.AssignmentTree;
import com.sun.source.tree.CatchTree;
import com.sun.source.tree.ClassTree;
import com.sun.source.tree.DoWhileLoopTree;
import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.ExpressionTree;
import com.sun.source.tree.ForLoopTree;
import com.sun.source.tree.LiteralTree;
import com.sun.source.tree.MemberSelectTree;
import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.StatementTree;
import com.sun.source.tree.Tree;
import com.sun.source.tree.TryTree;
import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.source.util.TreeScanner;
import com.sun.tools.javac.code.Symbol;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Pattern;

import javax.lang.model.element.Name;

/**
 * A bug checker for the following code pattern:
 *
 *

 * try {
 *   ... do something that should throw ...
 *   // forget to call Assert.fail()
 * } catch (SomeException expected) {
 *   // asserts on the exception or comments
 * }
 *

 *
 *
Detection

 * This checker uses heuristics that identify as many occurrences of the problem as possible while
 * keeping the false positive rate low (low single-digit percentages).
 *
 *
Synonyms

 * Many test writers don't seem to know about {@code fail()}. They instead use synonyms of varying
 * complexity instead. A large class of synonyms replaces {@code fail()} with a single, equivalent
 * statement such as {@code assertTrue(false)}, {@code assertFalse(true)} or {@code throw
 * new AssertionError()}. In these cases we will simply skip any further analysis, they work
 * perfectly fine.
 *
 *
Other, more complex synonyms tend to use boolean variables, like such:
 *

 * boolean thrown = false;
 * try {
 *   throwingExpression();
 * } catch (SomeException e) {
 *   thrown = true;
 * }
 * assertTrue(thrown);
 *

 *
 *
In these complex cases, the checker treats them as if they contained no detection of the
 * missing exception and shows the usual suggestion to use {@code fail()} instead. This should alert
 * the writer that their test can be simplified.
 *
 *
Heuristics

 * Five methods were explored to detect missing {@code fail()} calls, triggering if no {@code
 * fail()} is used in a {@code try/catch} statement within a JUnit test class:
 *
 *

 *
Cases in which the caught exception is called "expected".
 *
Cases in which there is a call to an {@code assert*()} method in the catch block.
 *
Cases in which "expected" shows up in a comment inside the {@code catch} block.
 *
Cases in which the {@code catch} block is empty.
 *
Cases in which the {@code try} block contains only a single statement.
 *

 *
 *
Only the first three yield useful results and also required some more refinement to reduce
 * false positives. In addition, the checker does not trigger on comments in the {@code catch} block
 * due to implementation complexity.
 *
 *
To reduce false positives, no match is found if any of the following is true:
 *
 *

 *
Any method with {@code fail} in its name is present in either catch or try block.
 *
A {@code throw} statement or synonym ({@code assertTrue(false)}, etc.) is present in either
 *       {@code catch} or {@code try} block.
 *
The occurrence happens inside a {@code setUp}, {@code tearDown},
 *       {@literal @}{@code Before}, {@literal @}{@code After}, {@code suite} or{@code main} method.
 *
The method returns from the {@code try} or {@code catch} block or immediately after.
 *
The exception caught is of type {@link InterruptedException}, {@link AssertionError},
 *       {@link junit.framework.AssertionFailedError} or {@link Throwable}.
 *
The occurrence is inside a loop.
 *
The try block contains a {@code while\(true\)} loop.
 *
The {@code try} or {@code catch} block contains a {@code continue;} statement.
 *
The {@code try/catch} statement also contains a {@code finally} statement.
 *
A logging call is present in the {@code catch} block.
 *

 *
 *
In addition, for occurrences which matched because they have a call to an {@code assert*()}
 * method in the catch block, no match is found if any of the following characteristics are present:
 *
 *

 *
A field assignment in the catch block.
 *
A call to {@code assertTrue/False(boolean variable or field)} in the catch block.
 *
The last statement in the {@code try} block is an {@code assert*()} (that is not a noop:
 *       {@code assertFalse(false)}, {@code assertTrue(true))} or {@code Mockito.verify()} call.
 *

 *
 * @author schmitt@google.com (Peter Schmitt)
 */
@BugPattern(name = "MissingFail",
    altNames = "missing-fail",
    summary = "Not calling fail() when expecting an exception masks bugs",
    explanation =
          "When testing for exceptions in junit, it is easy to forget the call to `fail()`:\n\n"
        + "    try {\n"
        + "      ... do something that should throw ...\n"
        + "      // forget to call Assert.fail()\n"
        + "    } catch (SomeException expected) {\n"
        + "      // asserts on the exception or comments\n"
        + "    }\n\n"
        + "Without this call, this test will always pass, regardless of whether an exception is "
        + "thrown or not: It is a noop.",
    category = JUNIT,
    maturity = EXPERIMENTAL,
    severity = WARNING)
public class MissingFail extends BugChecker implements TryTreeMatcher {

  private static final Matcher<ExpressionTree> ASSERT_EQUALS = Matchers.anyOf(
      staticMethod().onClass("org.junit.Assert").named("assertEquals"),
      staticMethod().onClass("junit.framework.Assert").named("assertEquals"),
      staticMethod().onClass("junit.framework.TestCase").named("assertEquals"));
  private static final Matcher<Tree> ASSERT_UNEQUAL =
      toType(MethodInvocationTree.class, new UnequalIntegerLiteralMatcher(ASSERT_EQUALS));

  private static final Matcher<ExpressionTree> ASSERT_TRUE = Matchers.anyOf(
      staticMethod().onClass("org.junit.Assert").named("assertTrue"),
      staticMethod().onClass("junit.framework.Assert").named("assertTrue"),
      staticMethod().onClass("junit.framework.TestCase").named("assertTrue"));
  private static final Matcher<ExpressionTree> ASSERT_FALSE = Matchers.anyOf(
      staticMethod().onClass("org.junit.Assert").named("assertFalse"),
      staticMethod().onClass("junit.framework.Assert").named("assertFalse"),
      staticMethod().onClass("junit.framework.TestCase").named("assertFalse"));
  private static final Matcher<ExpressionTree> ASSERT_TRUE_FALSE =
      methodInvocation(ASSERT_TRUE, MatchType.AT_LEAST_ONE,
          Matchers.anyOf(booleanLiteral(false), booleanConstant(false)));
  private static final Matcher<ExpressionTree> ASSERT_FALSE_TRUE =
      methodInvocation(ASSERT_FALSE, MatchType.AT_LEAST_ONE, Matchers.anyOf(
          booleanLiteral(true), booleanConstant(true)));
  private static final Matcher<ExpressionTree> ASSERT_TRUE_TRUE =
      methodInvocation(ASSERT_TRUE, MatchType.AT_LEAST_ONE, Matchers.anyOf(
          booleanLiteral(true), booleanConstant(true)));
  private static final Matcher<ExpressionTree> ASSERT_FALSE_FALSE =
      methodInvocation(ASSERT_FALSE, MatchType.AT_LEAST_ONE, Matchers.anyOf(
          booleanLiteral(false), booleanConstant(false)));

  private static final Matcher<StatementTree> JAVA_ASSERT_FALSE =
      assertStatement(ignoreParens(Matchers.anyOf(booleanLiteral(false), booleanConstant(false))));

  private static final Matcher<ExpressionTree> LOG_CALL = methodInvocation(new LogMethodMatcher());
  private static final Matcher<Tree> LOG_IN_BLOCK =
      new Contains(toType(ExpressionTree.class, LOG_CALL));

  private static final Pattern FAIL_PATTERN = Pattern.compile(".*(?i:fail).*");
  private static final Matcher<ExpressionTree> FAIL =
      anyMethod().anyClass().withNameMatching(FAIL_PATTERN);

  private static final Matcher<ExpressionTree> ASSERT_CALL =
      methodInvocation(new AssertMethodMatcher());
  private static final Matcher<ExpressionTree> REAL_ASSERT_CALL = Matchers.allOf(
      ASSERT_CALL,
      Matchers.not(Matchers.anyOf(ASSERT_FALSE_FALSE, ASSERT_TRUE_TRUE)));
  private static final Matcher<ExpressionTree> VERIFY_CALL =
      methodInvocation(staticMethod().onClass("org.mockito.Mockito").named("verify"));
  private static final MultiMatcher<TryTree, Tree> ASSERT_LAST_CALL_IN_TRY =
      new ChildOfTryMatcher(MatchType.LAST, new Contains(toType(ExpressionTree.class,
          Matchers.anyOf(REAL_ASSERT_CALL, VERIFY_CALL))));
  private static final Matcher<Tree> ASSERT_IN_BLOCK =
      new Contains(toType(ExpressionTree.class, REAL_ASSERT_CALL));

  private static final Matcher<StatementTree> THROW_STATEMENT = throwStatement(Matchers.anything());
  private static final Matcher<Tree> THROW_OR_FAIL_IN_BLOCK = new Contains(
      Matchers.anyOf(
          toType(StatementTree.class, THROW_STATEMENT),
          // TODO(user): Include Preconditions.checkState(false)?
          toType(ExpressionTree.class, ASSERT_TRUE_FALSE),
          toType(ExpressionTree.class, ASSERT_FALSE_TRUE),
          toType(ExpressionTree.class, ASSERT_UNEQUAL),
          toType(StatementTree.class, JAVA_ASSERT_FALSE),
          toType(ExpressionTree.class, FAIL)));

  private static final Matcher<TryTree> NON_TEST_METHOD = new IgnoredEnclosingMethodMatcher();

  private static final Matcher<Tree> RETURN_IN_BLOCK = new Contains(
      toType(StatementTree.class, returnStatement(Matchers.anything())));
  private static final NextStatement<StatementTree> RETURN_AFTER =
      nextStatement(returnStatement(Matchers.anything()));

  private static final Matcher<VariableTree> INAPPLICABLE_EXCEPTION = Matchers.anyOf(
      isSameType("java.lang.InterruptedException"),
      isSameType("java.lang.AssertionError"),
      isSameType("java.lang.Throwable"),
      isSameType("junit.framework.AssertionFailedError"));

  private static final InLoopMatcher IN_LOOP = new InLoopMatcher();

  private static final Matcher<Tree> WHILE_TRUE_IN_BLOCK =
      new Contains(toType(WhileLoopTree.class, new WhileTrueLoopMatcher()));

  private static final Matcher<Tree> CONTINUE_IN_BLOCK =
      new Contains(toType(StatementTree.class, continueStatement()));

  private static final Matcher<AssignmentTree> FIELD_ASSIGNMENT =
      assignment(isInstanceField(), Matchers.<ExpressionTree>anything());
  private static final Matcher<Tree> FIELD_ASSIGNMENT_IN_BLOCK =
      new Contains(toType(AssignmentTree.class, FIELD_ASSIGNMENT));

  private static final Matcher<ExpressionTree> BOOLEAN_ASSERT_VAR = methodInvocation(
      Matchers.anyOf(ASSERT_FALSE, ASSERT_TRUE),
      MatchType.AT_LEAST_ONE,
      Matchers.anyOf(isInstanceField(), isVariable()));
  private static final Matcher<Tree> BOOLEAN_ASSERT_VAR_IN_BLOCK =
      new Contains(toType(ExpressionTree.class, BOOLEAN_ASSERT_VAR));

  // Subtly different from JUnitMatchers: We want to match test base classes too.
  private static final Matcher<ClassTree> JUNIT3_TEST_CLASS =
      isSubtypeOf("junit.framework.TestCase");
  private static final Matcher<ClassTree> TEST_CLASS = Matchers.anyOf(JUNIT3_TEST_CLASS,
      new JUnitMatchers.JUnit4TestClassMatcher());

  @Override
  public Description matchTry(TryTree tree, VisitorState state) {
    if (tryTreeMatches(tree, state)) {
      List<? extends StatementTree> tryStatements = tree.getBlock().getStatements();
      StatementTree lastTryStatement = tryStatements.get(tryStatements.size() - 1);

      String failCall = String.format("%nfail(\"Expected %s\");", exceptionToString(tree));
      SuggestedFix.Builder fixBuilder = SuggestedFix.builder()
          .postfixWith(lastTryStatement, failCall);

      // Make sure that when the fail import is added it doesn't conflict with existing ones.
      fixBuilder.removeStaticImport("junit.framework.Assert.fail");
      fixBuilder.removeStaticImport("junit.framework.TestCase.fail");
      fixBuilder.addStaticImport("org.junit.Assert.fail");

      return describeMatch(lastTryStatement, fixBuilder.build());
    } else {
      return Description.NO_MATCH;
    }
  }

  /**
   * Returns a string describing the exception type caught by the given try tree's catch
   * statement(s), defaulting to {@code "Exception"} if more than one exception type is caught.
   */
  private String exceptionToString(TryTree tree) {
    if (tree.getCatches().size() != 1) {
      return "Exception";
    }
    String exceptionType = tree.getCatches().iterator().next().getParameter().getType().toString();
    if (exceptionType.contains("|")) {
      return "Exception";
    }
    return exceptionType;
  }

  private boolean tryTreeMatches(TryTree tree, VisitorState state) {
    if (!isInClass(tree, state, TEST_CLASS)) {
      return false;
    }

    boolean assertInCatch = hasAssertInCatch(tree, state);
    if (!hasExpectedException(tree) && !assertInCatch) {
      return false;
    }

    if (hasThrowOrFail(tree, state) || isInInapplicableMethod(tree, state)
        || returnsInTryCatchOrAfter(tree, state) || isInapplicableExceptionType(tree, state)
        || isInLoop(state, tree) || hasWhileTrue(tree, state) || hasContinue(tree, state)
        || hasFinally(tree) || logsInCatch(state, tree)) {
      return false;
    }

    if (assertInCatch
        && (hasFieldAssignmentInCatch(tree, state)
            || hasBooleanAssertVariableInCatch(tree, state)
            || lastTryStatementIsAssert(tree, state))) {
      return false;
    }

    return true;
  }

  private boolean hasWhileTrue(TryTree tree, VisitorState state) {
    return WHILE_TRUE_IN_BLOCK.matches(tree, state);
  }

  private boolean isInClass(TryTree tree, VisitorState state, Matcher<ClassTree> classTree) {
    return Matchers.enclosingNode(toType(ClassTree.class, classTree)).matches(tree, state);
  }

  private boolean hasBooleanAssertVariableInCatch(TryTree tree, VisitorState state) {
    return anyCatchBlockMatches(tree, state, BOOLEAN_ASSERT_VAR_IN_BLOCK);
  }

  private boolean lastTryStatementIsAssert(TryTree tree, VisitorState state) {
    return ASSERT_LAST_CALL_IN_TRY.matches(tree, state);
  }

  private boolean hasFieldAssignmentInCatch(TryTree tree, VisitorState state) {
    return anyCatchBlockMatches(tree, state, FIELD_ASSIGNMENT_IN_BLOCK);
  }

  private boolean logsInCatch(VisitorState state, TryTree tree) {
    return anyCatchBlockMatches(tree, state, LOG_IN_BLOCK);
  }

  private boolean hasFinally(TryTree tree) {
    return tree.getFinallyBlock() != null;
  }

  private boolean hasContinue(TryTree tree, VisitorState state) {
    return CONTINUE_IN_BLOCK.matches(tree, state);
  }

  private boolean isInLoop(VisitorState state, TryTree tree) {
    return IN_LOOP.matches(tree, state);
  }

  private boolean isInapplicableExceptionType(TryTree tree, VisitorState state) {
    for (CatchTree catchTree : tree.getCatches()) {
      if (INAPPLICABLE_EXCEPTION.matches(catchTree.getParameter(), state)) {
        return true;
      }
    }
    return false;
  }

  private boolean returnsInTryCatchOrAfter(TryTree tree, VisitorState state) {
    return RETURN_IN_BLOCK.matches(tree, state) || RETURN_AFTER.matches(tree, state);
  }

  private boolean isInInapplicableMethod(TryTree tree, VisitorState state) {
    return NON_TEST_METHOD.matches(tree, state);
  }

  private boolean hasThrowOrFail(TryTree tree, VisitorState state) {
    return THROW_OR_FAIL_IN_BLOCK.matches(tree, state);
  }

  private boolean hasAssertInCatch(TryTree tree, VisitorState state) {
    return anyCatchBlockMatches(tree, state, ASSERT_IN_BLOCK);
  }

  private boolean hasExpectedException(TryTree tree) {
    for (CatchTree catchTree : tree.getCatches()) {
      if (catchTree.getParameter().getName().toString().equals("expected")) {
        return true;
      }
    }
    return false;
  }

  private boolean anyCatchBlockMatches(TryTree tree, VisitorState state, Matcher<Tree> matcher) {
    for (CatchTree catchTree : tree.getCatches()) {
      if (matcher.matches(catchTree.getBlock(), state)) {
        return true;
      }
    }
    return false;
  }

  private static class AssertMethodMatcher implements Matcher<ExpressionTree> {

    @Override
    public boolean matches(ExpressionTree expressionTree, VisitorState state) {
      Symbol sym = ASTHelpers.getSymbol(expressionTree);
      return sym != null && sym.getSimpleName().toString().startsWith("assert");
    }
  }

  private static class LogMethodMatcher implements Matcher<ExpressionTree> {

    @Override
    public boolean matches(ExpressionTree expressionTree, VisitorState state) {
      Symbol sym = ASTHelpers.getSymbol(expressionTree);
      if (sym != null && sym.getSimpleName().toString().startsWith("log")) {
        return true;
      }

      if (sym != null && sym.isStatic()) {
        if (sym.owner.getQualifiedName().toString().contains("Logger")) {
          return true;
        }
      } else if (expressionTree instanceof MemberSelectTree) {
        if (((MemberSelectTree) expressionTree).getExpression().toString().startsWith("log")) {
          return true;
        }
      }

      return false;
    }
  }

  /**
   * Matches any try-tree that is enclosed in a loop.
   */
  private static class InLoopMatcher implements Matcher<TryTree> {

    @Override
    public boolean matches(TryTree tryTree, VisitorState state) {
      return ASTHelpers.findEnclosingNode(state.getPath(), DoWhileLoopTree.class) != null
          || ASTHelpers.findEnclosingNode(state.getPath(), EnhancedForLoopTree.class) != null
          || ASTHelpers.findEnclosingNode(state.getPath(), WhileLoopTree.class) != null
          || ASTHelpers.findEnclosingNode(state.getPath(), ForLoopTree.class) != null;
    }
  }

  private static class WhileTrueLoopMatcher implements Matcher<WhileLoopTree> {

    @Override
    public boolean matches(WhileLoopTree tree, VisitorState state) {
      return ignoreParens(booleanLiteral(true))
          .matches(tree.getCondition(), state);
    }
  }

  /**
   * Matches any try-tree that is in a JUNit3 {@code setUp} or {@code tearDown} method, their JUnit4
   * equivalents, a JUnit {@code suite()} method or a {@code main} method.
   */
  private static class IgnoredEnclosingMethodMatcher implements Matcher<TryTree> {

    @Override
    public boolean matches(TryTree tryTree, VisitorState state) {
      MethodTree enclosingMethodTree =
          ASTHelpers.findEnclosingNode(state.getPath(), MethodTree.class);

      Name name = enclosingMethodTree.getName();
      return JUnitMatchers.looksLikeJUnit3SetUp.matches(enclosingMethodTree, state)
          || JUnitMatchers.looksLikeJUnit3TearDown.matches(enclosingMethodTree, state)
          || name.contentEquals("main")
          // TODO(user): Move to JUnitMatchers?
          || name.contentEquals("suite")
          || Matchers.hasAnnotation(JUNIT_BEFORE_ANNOTATION).matches(enclosingMethodTree, state)
          || Matchers.hasAnnotation(JUNIT_AFTER_ANNOTATION).matches(enclosingMethodTree, state);
    }
  }

  /**
   * Matches if any two of the given list of expressions are integer literals with different values.
   */
  private static class UnequalIntegerLiteralMatcher
      implements MultiMatcher<MethodInvocationTree, ExpressionTree> {

    private final Matcher<ExpressionTree> methodSelectMatcher;

    private UnequalIntegerLiteralMatcher(Matcher<ExpressionTree> methodSelectMatcher) {
      this.methodSelectMatcher = methodSelectMatcher;
    }

    @Override
    public boolean matches(MethodInvocationTree methodInvocationTree, VisitorState state) {
      return methodSelectMatcher.matches(methodInvocationTree, state)
          && matches(methodInvocationTree.getArguments());
    }

    private boolean matches(List<? extends ExpressionTree> expressionTrees) {
      Set<Integer> foundValues = new HashSet<>();
      for (Tree tree : expressionTrees) {
        if (tree instanceof LiteralTree) {
          Object value = ((LiteralTree) tree).getValue();
          if (value instanceof Integer) {
            boolean duplicate = !foundValues.add((Integer) value);
            if (!duplicate && foundValues.size() > 1) {
              return true;
            }
          }
        }
      }
      return false;
    }

    @Override
    public ExpressionTree getMatchingNode() {
      throw new UnsupportedOperationException();
    }
  }

  /**
   * A matcher that recursively inspects a tree, applying the given matcher to all levels of each
   * tree and returning {@code true} if any match is found.
   */
  private static class Contains implements Matcher<Tree> {

    private final Matcher<Tree> matcher;

    /**
     * Creates a new matcher that will recursively apply {@code matcher} to the input tree.
     */
    public Contains(Matcher<Tree> matcher) {
      this.matcher = matcher;
    }

    @Override
    public boolean matches(Tree tree, VisitorState state) {
      Boolean matchFound = tree.accept(new FirstMatchingScanner(state, matcher), false);
      return matchFound != null && matchFound;
    }

    private class FirstMatchingScanner extends TreeScanner<Boolean, Boolean> {

      private final VisitorState state;
      private final Matcher<Tree> matcher;

      public FirstMatchingScanner(VisitorState state, Matcher<Tree> matcher) {
        this.state = state;
        this.matcher = matcher;
      }

      @Override
      public Boolean scan(Tree tree, Boolean matchFound) {
        if (matchFound) {
          return true;
        }

        if (matcher.matches(tree, state)) {
          return true;
        }
        return super.scan(tree, false);
      }

      @Override
      public Boolean reduce(Boolean left, Boolean right) {
        return (left != null && left) || (right != null && right);
      }
    }
  }

  private static class ChildOfTryMatcher extends ChildMultiMatcher<TryTree, Tree> {

    public ChildOfTryMatcher(MatchType matchType, Matcher<Tree> nodeMatcher) {
      super(matchType, nodeMatcher);
    }

    @Override
    protected Iterable<? extends StatementTree> getChildNodes(TryTree tree, VisitorState state) {
      return tree.getBlock().getStatements();
    }
  }
}


text:
require 'spec_helper'

describe RuboCop::AST::SendNode do
  let(:send_node) { parse_source(source).ast }

  describe '.new' do
    let(:source) { 'foo.bar(:baz)' }

    it { expect(send_node).to be_a(described_class) }
  end

  describe '#receiver' do
    context 'with no receiver' do
      let(:source) { 'bar(:baz)' }

      it { expect(send_node.receiver).to be_nil }
    end

    context 'with a literal receiver' do
      let(:source) { "'foo'.bar(:baz)" }

      it { expect(send_node.receiver).to be_str_type }
    end

    context 'with a variable receiver' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.receiver).to be_send_type }
    end
  end

  describe '#method_name' do
    context 'with a plain method' do
      let(:source) { 'bar(:baz)' }

      it { expect(send_node.method_name).to eq(:bar) }
    end

    context 'with a setter method' do
      let(:source) { 'foo.bar = :baz' }

      it { expect(send_node.method_name).to eq(:bar=) }
    end

    context 'with an operator method' do
      let(:source) { 'foo == bar' }

      it { expect(send_node.method_name).to eq(:==) }
    end

    context 'with an implicit call method' do
      let(:source) { 'foo.(:baz)' }

      it { expect(send_node.method_name).to eq(:call) }
    end
  end

  describe '#method?' do
    context 'when message matches' do
      context 'when argument is a symbol' do
        let(:source) { 'bar(:baz)' }

        it { expect(send_node.method?(:bar)).to be_truthy }
      end

      context 'when argument is a string' do
        let(:source) { 'bar(:baz)' }

        it { expect(send_node.method?('bar')).to be_truthy }
      end
    end

    context 'when message does not match' do
      context 'when argument is a symbol' do
        let(:source) { 'bar(:baz)' }

        it { expect(send_node.method?(:foo)).to be_falsey }
      end

      context 'when argument is a string' do
        let(:source) { 'bar(:baz)' }

        it { expect(send_node.method?('foo')).to be_falsey }
      end
    end
  end

  describe '#command?' do
    context 'when argument is a symbol' do
      context 'with an explicit receiver' do
        let(:source) { 'foo.bar(:baz)' }

        it { expect(send_node.command?(:bar)).to be_falsey }
      end

      context 'with an implicit receiver' do
        let(:source) { 'bar(:baz)' }

        it { expect(send_node.command?(:bar)).to be_truthy }
      end
    end

    context 'when argument is a string' do
      context 'with an explicit receiver' do
        let(:source) { 'foo.bar(:baz)' }

        it { expect(send_node.command?('bar')).to be_falsey }
      end

      context 'with an implicit receiver' do
        let(:source) { 'bar(:baz)' }

        it { expect(send_node.command?('bar')).to be_truthy }
      end
    end
  end

  describe '#arguments' do
    context 'with no arguments' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.arguments).to be_empty }
    end

    context 'with a single literal argument' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.arguments.size).to eq(1) }
    end

    context 'with a single splat argument' do
      let(:source) { 'foo.bar(*baz)' }

      it { expect(send_node.arguments.size).to eq(1) }
    end

    context 'with multiple literal arguments' do
      let(:source) { 'foo.bar(:baz, :qux)' }

      it { expect(send_node.arguments.size).to eq(2) }
    end

    context 'with multiple mixed arguments' do
      let(:source) { 'foo.bar(:baz, *qux)' }

      it { expect(send_node.arguments.size).to eq(2) }
    end
  end

  describe '#first_argument' do
    context 'with no arguments' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.first_argument).to be_nil }
    end

    context 'with a single literal argument' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.first_argument).to be_sym_type }
    end

    context 'with a single splat argument' do
      let(:source) { 'foo.bar(*baz)' }

      it { expect(send_node.first_argument).to be_splat_type }
    end

    context 'with multiple literal arguments' do
      let(:source) { 'foo.bar(:baz, :qux)' }

      it { expect(send_node.first_argument).to be_sym_type }
    end

    context 'with multiple mixed arguments' do
      let(:source) { 'foo.bar(:baz, *qux)' }

      it { expect(send_node.first_argument).to be_sym_type }
    end
  end

  describe '#last_argument' do
    context 'with no arguments' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.last_argument).to be_nil }
    end

    context 'with a single literal argument' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.last_argument).to be_sym_type }
    end

    context 'with a single splat argument' do
      let(:source) { 'foo.bar(*baz)' }

      it { expect(send_node.last_argument).to be_splat_type }
    end

    context 'with multiple literal arguments' do
      let(:source) { 'foo.bar(:baz, :qux)' }

      it { expect(send_node.last_argument).to be_sym_type }
    end

    context 'with multiple mixed arguments' do
      let(:source) { 'foo.bar(:baz, *qux)' }

      it { expect(send_node.last_argument).to be_splat_type }
    end
  end

  describe '#arguments?' do
    context 'with no arguments' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.arguments?).to be_falsey }
    end

    context 'with a single literal argument' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.arguments?).to be_truthy }
    end

    context 'with a single splat argument' do
      let(:source) { 'foo.bar(*baz)' }

      it { expect(send_node.arguments?).to be_truthy }
    end

    context 'with multiple literal arguments' do
      let(:source) { 'foo.bar(:baz, :qux)' }

      it { expect(send_node.arguments?).to be_truthy }
    end

    context 'with multiple mixed arguments' do
      let(:source) { 'foo.bar(:baz, *qux)' }

      it { expect(send_node.arguments?).to be_truthy }
    end
  end

  describe '#parenthesized?' do
    context 'with no arguments' do
      context 'when not using parentheses' do
        let(:source) { 'foo.bar' }

        it { expect(send_node.parenthesized?).to be_falsey }
      end

      context 'when using parentheses' do
        let(:source) { 'foo.bar()' }

        it { expect(send_node.parenthesized?).to be_truthy }
      end
    end

    context 'with arguments' do
      context 'when not using parentheses' do
        let(:source) { 'foo.bar :baz' }

        it { expect(send_node.parenthesized?).to be_falsey }
      end

      context 'when using parentheses' do
        let(:source) { 'foo.bar(:baz)' }

        it { expect(send_node.parenthesized?).to be_truthy }
      end
    end
  end

  describe '#setter_method?' do
    context 'with a setter method' do
      let(:source) { 'foo.bar = :baz' }

      it { expect(send_node.setter_method?).to be_truthy }
    end

    context 'with an indexed setter method' do
      let(:source) { 'foo.bar[:baz] = :qux' }

      it { expect(send_node.setter_method?).to be_truthy }
    end

    context 'with an operator method' do
      let(:source) { 'foo.bar + 1' }

      it { expect(send_node.setter_method?).to be_falsey }
    end

    context 'with a regular method' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.setter_method?).to be_falsey }
    end
  end

  describe '#operator_method?' do
    context 'with a binary operator method' do
      let(:source) { 'foo.bar == :baz' }

      it { expect(send_node.operator_method?).to be_truthy }
    end

    context 'with a unary operator method' do
      let(:source) { '!foo.bar' }

      it { expect(send_node.operator_method?).to be_truthy }
    end

    context 'with a setter method' do
      let(:source) { 'foo.bar = :baz' }

      it { expect(send_node.operator_method?).to be_falsey }
    end

    context 'with a regular method' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.operator_method?).to be_falsey }
    end
  end

  describe '#dot?' do
    context 'with a dot' do
      let(:source) { 'foo.+ 1' }

      it { expect(send_node.dot?).to be_truthy }
    end

    context 'without a dot' do
      let(:source) { 'foo + 1' }

      it { expect(send_node.dot?).to be_falsey }
    end

    context 'with a double colon' do
      let(:source) { 'Foo::bar' }

      it { expect(send_node.dot?).to be_falsey }
    end

    context 'with a unary method' do
      let(:source) { '!foo.bar' }

      it { expect(send_node.dot?).to be_falsey }
    end
  end

  describe '#double_colon?' do
    context 'with a double colon' do
      let(:source) { 'Foo::bar' }

      it { expect(send_node.double_colon?).to be_truthy }
    end

    context 'with a dot' do
      let(:source) { 'foo.+ 1' }

      it { expect(send_node.double_colon?).to be_falsey }
    end

    context 'without a dot' do
      let(:source) { 'foo + 1' }

      it { expect(send_node.double_colon?).to be_falsey }
    end

    context 'with a unary method' do
      let(:source) { '!foo.bar' }

      it { expect(send_node.double_colon?).to be_falsey }
    end
  end

  describe '#self_receiver?' do
    context 'with a self receiver' do
      let(:source) { 'self.bar' }

      it { expect(send_node.self_receiver?).to be_truthy }
    end

    context 'with a non-self receiver' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.self_receiver?).to be_falsey }
    end

    context 'with an implicit receiver' do
      let(:source) { 'bar' }

      it { expect(send_node.self_receiver?).to be_falsey }
    end
  end

  describe '#implicit_call?' do
    context 'with an implicit call method' do
      let(:source) { 'foo.(:bar)' }

      it { expect(send_node.implicit_call?).to be_truthy }
    end

    context 'with an explicit call method' do
      let(:source) { 'foo.call(:bar)' }

      it { expect(send_node.implicit_call?).to be_falsey }
    end

    context 'with a regular method' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.implicit_call?).to be_falsey }
    end
  end

  describe '#predicate_method?' do
    context 'with a predicate method' do
      let(:source) { 'foo.bar?' }

      it { expect(send_node.predicate_method?).to be_truthy }
    end

    context 'with a bang method' do
      let(:source) { 'foo.bar!' }

      it { expect(send_node.predicate_method?).to be_falsey }
    end

    context 'with a regular method' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.predicate_method?).to be_falsey }
    end
  end

  describe '#bang_method?' do
    context 'with a bang method' do
      let(:source) { 'foo.bar!' }

      it { expect(send_node.bang_method?).to be_truthy }
    end

    context 'with a predicate method' do
      let(:source) { 'foo.bar?' }

      it { expect(send_node.bang_method?).to be_falsey }
    end

    context 'with a regular method' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.bang_method?).to be_falsey }
    end
  end

  describe '#camel_case_method?' do
    context 'with a camel case method' do
      let(:source) { 'Integer(1.0)' }

      it { expect(send_node.camel_case_method?).to be_truthy }
    end

    context 'with a regular method' do
      let(:source) { 'integer(1.0)' }

      it { expect(send_node.camel_case_method?).to be_falsey }
    end
  end

  describe '#block_argument?' do
    context 'with a block argument' do
      let(:source) { 'foo.bar(&baz)' }

      it { expect(send_node.block_argument?).to be_truthy }
    end

    context 'with no arguments' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.block_argument?).to be_falsey }
    end

    context 'with regular arguments' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.block_argument?).to be_falsey }
    end

    context 'with mixed arguments' do
      let(:source) { 'foo.bar(:baz, &qux)' }

      it { expect(send_node.block_argument?).to be_truthy }
    end
  end

  describe '#splat_argument?' do
    context 'with a splat argument' do
      let(:source) { 'foo.bar(*baz)' }

      it { expect(send_node.splat_argument?).to be_truthy }
    end

    context 'with no arguments' do
      let(:source) { 'foo.bar' }

      it { expect(send_node.splat_argument?).to be_falsey }
    end

    context 'with regular arguments' do
      let(:source) { 'foo.bar(:baz)' }

      it { expect(send_node.splat_argument?).to be_falsey }
    end

    context 'with mixed arguments' do
      let(:source) { 'foo.bar(:baz, *qux)' }

      it { expect(send_node.splat_argument?).to be_truthy }
    end
  end
end

redpajama_set_name: RedPajamaGithub


text:
Disallows type arguments that are equal to the default.

Warns if an explicitly specified type argument is the default for that type parameter.

## Rule Details

Type parameters in TypeScript may specify a default value.
For example:

```ts
function f<T = number>() {}
```

It is redundant to provide an explicit type parameter equal to that default.

Examples of code for this rule:

<!--tabs-->

### ❌ Incorrect

```ts
function f<T = number>() {}
f<number>();

function g<T = number, U = string>() {}
g<string, string>();

class C<T = number> {}
function h(c: C<number>) {}
new C<number>();
class D extends C<number> {}

interface I<T = number> {}
class Impl implements I<number> {}
```

### ✅ Correct

```ts
function f<T = number>() {}
f<string>();

function g<T = number, U = string>() {}
g<number, number>();

class C<T = number> {}
new C<string>();
class D extends C<string> {}

interface I<T = number> {}
class Impl implements I<string> {}
```

## Options

```jsonc
// .eslintrc.json
{
  "rules": {
    "@typescript-eslint/no-unnecessary-type-arguments": "warn"
  }
}
```

This rule is not configurable.


text:
package com.soomla.keeva;

/**
 * This class holds the storage's configurations.
 *
 * IMPORTANT: Change obfuscationSalt below !!!
 * IMPORTANT: Before releasing your game, set DB_DELETE to false !!!
 */
public class KeevaConfig {

    //Set to true if you want to print out debug messages
    public static boolean logDebug = false;

    /**
     * The obfuscated salt: randomly generated numbers.
     * IMPORTANT: it's recommended that you change these numbers for your specific application,
     * BUT change them only once!
     */
    public static final byte[] obfuscationSalt = new byte[] { 64, -54, -113, -47, 98, -52, 87,
            -102, -65, -127, 89, 51, -11, -35, 30, 77, -45, 75, -26, 3 };

    /**
     * If this is true then the database will be deleted whenever the application loads.
     *
     * WARNING: Do NOT release your game with this option set to true!
     * Otherwise, your users will lose all their data every time they load the application.
     *
     * NOTE: this feature can be useful for testing when you want to change stuff in your
     * implementation of IStoreAssets and see what they look like. If you try to
     * change things in IStoreAssets and don't delete the DB then your changes will
     * not be shown.
     */
    public static final boolean DB_DELETE = false;

}


text:
.class final synthetic Landroid/app/-$Lambda$vZ1qb742P9hE4drBY-TrOZB_qKo;
.super Ljava/lang/Object;

# interfaces
.implements Landroid/app/PendingIntent$OnMarshaledListener;

# instance fields
.field private final synthetic -$f0:Ljava/lang/Object;

.field private final synthetic -$f1:Ljava/lang/Object;

# direct methods
.method private final synthetic $m$0(Landroid/app/PendingIntent;Landroid/os/Parcel;I)V
    .locals 2

    iget-object v0, p0, Landroid/app/-$Lambda$vZ1qb742P9hE4drBY-TrOZB_qKo;->-$f0:Ljava/lang/Object;

    check-cast v0, Landroid/app/Notification;

    iget-object v1, p0, Landroid/app/-$Lambda$vZ1qb742P9hE4drBY-TrOZB_qKo;->-$f1:Ljava/lang/Object;

    check-cast v1, Landroid/os/Parcel;

    invoke-virtual {v0, v1, p1, p2, p3}, Landroid/app/Notification;->lambda$-android_app_Notification_85781(Landroid/os/Parcel;Landroid/app/PendingIntent;Landroid/os/Parcel;I)V

    return-void
.end method

.method public synthetic constructor <init>(Ljava/lang/Object;Ljava/lang/Object;)V
    .locals 0

    invoke-direct {p0}, Ljava/lang/Object;-><init>()V

    iput-object p1, p0, Landroid/app/-$Lambda$vZ1qb742P9hE4drBY-TrOZB_qKo;->-$f0:Ljava/lang/Object;

    iput-object p2, p0, Landroid/app/-$Lambda$vZ1qb742P9hE4drBY-TrOZB_qKo;->-$f1:Ljava/lang/Object;

    return-void
.end method

# virtual methods
.method public final onMarshaled(Landroid/app/PendingIntent;Landroid/os/Parcel;I)V
    .locals 0

    invoke-direct {p0, p1, p2, p3}, Landroid/app/-$Lambda$vZ1qb742P9hE4drBY-TrOZB_qKo;->$m$0(Landroid/app/PendingIntent;Landroid/os/Parcel;I)V

    return-void
.end method


text:
Serializer for PHP
=========================

[![Build Status](https://travis-ci.org/nilportugues/php-serializer.svg)](https://travis-ci.org/nilportugues/php-serializer)
[![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/nilportugues/serializer/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/nilportugues/serializer/?branch=master)  [![SensioLabsInsight](https://insight.sensiolabs.com/projects/7ae05bba-985d-4359-8a00-3209f85f1d77/mini.png?)](https://insight.sensiolabs.com/projects/7ae05bba-985d-4359-8a00-3209f85f1d77)
[![Latest Stable Version](https://poser.pugx.org/nilportugues/serializer/v/stable)](https://packagist.org/packages/nilportugues/serializer)
[![Total Downloads](https://poser.pugx.org/nilportugues/serializer/downloads)](https://packagist.org/packages/nilportugues/serializer) [![License](https://poser.pugx.org/nilportugues/serializer/license)](https://packagist.org/packages/nilportugues/serializer)
[![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_SM.gif)](https://paypal.me/nilportugues)

- [Installation](#installation)
- [Introduction](#introduction)
- [Features](#features)
- [Serialization](#serialization)
  - [Serializers (JSON, XML, YAML)](#serializers-json-xml-yaml)
   - [Example](#example)
   - [Custom Serializers](#custom-serializers)
- [Data Transformation](#data-transformation)
   - [Array Transformer](#array-transformer)
   - [Flat Array Transformer](#flat-array-transformer)
   - [XML Transformer](#xml-transformer)
   - [YAML Transformer](#yaml-transformer)
   - [JSON Transformer](#json-transformer)
   - [JSend Transformer](#jsend-transformer)
   - [JSON API Transformer](#json-api-transformer)
   - [HAL+JSON Transformer](#haljson-transformer)
- [Quality](#quality)
- [Contribute](#contribute)
- [Author](#author)
- [License](#license)

## Installation

Use [Composer](https://getcomposer.org) to install the package:

```json
$ composer require nilportugues/serializer
```

## Introduction

**What is serialization?**

In the context of data storage, serialization is the process of translating data structures or object state into a format that can be stored (for example, in a file or memory buffer, or transmitted across a network connection link) and reconstructed later in the same or another computer environment.

**Why not `serialize()` and `unserialize()`?**

These native functions rely on having the serialized classes loaded and available at runtime and tie your unserialization process to a `PHP` platform.

If the serialized string contains a reference to a class that cannot be instantiated (e.g. class was renamed, moved namespace, removed or changed to abstract) PHP will immediately die with a fatal error.

Is this a problem? Yes it is. Serialized data is now **unusable**.

## Features

- Serialize to **JSON**, **XML** and **YAML** formats.
- Serializes **exact copies** of the object provided:
 - **All object properties**, public, protected and private are serialized.
 - All properties from the current object, and all the inherited properties are read and serialized.
- Handles internal class serialization for objects such as SplFixedArray or classes implementing Traversable.
- Basic **Data Transformers provided** to convert objects to different output formats.
- **Production-ready**.
- **Extensible:** easily write your out `Serializer` format or data `Transformers`.

## Serialization
For the serializer to work, all you need to do is pass in a PHP Object to the serializer and a Strategy to implement its string representation.

### Serializers (JSON, XML, YAML)

- [NilPortugues\Serializer\JsonSerializer](https://github.com/nilportugues/serializer/blob/master/src/JsonSerializer.php)
- [NilPortugues\Serializer\XmlSerializer](https://github.com/nilportugues/serializer/blob/master/src/XmlSerializer.php)
- [NilPortugues\Serializer\YamlSerializer](https://github.com/nilportugues/serializer/blob/master/src/YamlSerializer.php)

### Example

In the following example a `$post` object is serialized into JSON.

**Code**

```php
use NilPortugues\Serializer\Serializer;
use NilPortugues\Serializer\Strategy\JsonStrategy;

//Example object
$post = new Post(
  new PostId(9),
  'Hello World',
  'Your first post',
  new User(
      new UserId(1),
      'Post Author'
  ),
  [
      new Comment(
          new CommentId(1000),
          'Have no fear, sers, your king is safe.',
          new User(new UserId(2), 'Barristan Selmy'),
          [
              'created_at' => (new DateTime('2015/07/18 12:13:00'))->format('c'),
              'accepted_at' => (new DateTime('2015/07/19 00:00:00'))->format('c'),
          ]
      ),
  ]
);

//Serialization
$serializer = new JsonSerializer();

$serializedObject = $serializer->serialize($post);

//Returns: true
var_dump($post == $serializer->unserialize($serializedObject));

echo $serializedObject;
```

The object, before it's transformed into an output format, is an array with all the necessary data to be rebuild using unserialize method.

**Output**

```json
{
    "@type": "Acme\\\\Domain\\\\Dummy\\\\Post",
    "postId": {
        "@type": "Acme\\\\Domain\\\\Dummy\\\\ValueObject\\\\PostId",
        "postId": {
            "@scalar": "integer",
            "@value": 14
        }
    },
    "title": {
        "@scalar": "string",
        "@value": "Hello World"
    },
    "content": {
        "@scalar": "string",
        "@value": "Your first post"
    },
    "author": {
        "@type": "Acme\\\\Domain\\\\Dummy\\\\User",
        "userId": {
            "@type": "Acme\\\\Domain\\\\Dummy\\\\ValueObject\\\\UserId",
            "userId": {
                "@scalar": "integer",
                "@value": 1
            }
        },
        "name": {
            "@scalar": "string",
            "@value": "Post Author"
        }
    },
    "comments": {
        "@map": "array",
        "@value": [
            {
                "@type": "Acme\\\\Domain\\\\Dummy\\\\Comment",
                "commentId": {
                    "@type": "Acme\\\\Domain\\\\Dummy\\\\ValueObject\\\\CommentId",
                    "commentId": {
                        "@scalar": "integer",
                        "@value": 1000
                    }
                },
                "dates": {
                    "@map": "array",
                    "@value": {
                        "created_at": {
                            "@scalar": "string",
                            "@value": "2015-07-18T12:13:00+00:00"
                        },
                        "accepted_at": {
                            "@scalar": "string",
                            "@value": "2015-07-19T00:00:00+00:00"
                        }
                    }
                },
                "comment": {
                    "@scalar": "string",
                    "@value": "Have no fear, sers, your king is safe."
                },
                "user": {
                    "@type": "Acme\\\\Domain\\\\Dummy\\\\User",
                    "userId": {
                        "@type": "Acme\\\\Domain\\\\Dummy\\\\ValueObject\\\\UserId",
                        "userId": {
                            "@scalar": "integer",
                            "@value": 2
                        }
                    },
                    "name": {
                        "@scalar": "string",
                        "@value": "Barristan Selmy"
                    }
                }
            }
        ]
    }
}'
```

### Custom Serializers

If a custom serialization strategy is preferred, the `Serializer` class should be used instead. A `CustomStrategy` must implement the `StrategyInterface`.

Usage is as follows:

```php
use NilPortugues\Serializer\Serializer;
use NilPortugues\Serializer\Strategy\CustomStrategy;

$serializer = new Serializer(new CustomStrategy());

echo $serializer->serialize($post);
```

----

## Data Transformation

Transformer classes **greatly differ** from a `Strategy` class because these cannot `unserialize()` as all class references are lost in the process of transformation.

To obtain transformations instead of the `Serializer` class usage of `DeepCopySerializer` is required.

The Serializer library comes with a set of defined Transformers that implement the `StrategyInterface`.
Usage is as simple as before, pass a Transformer as a `$strategy`.

**For instance:**

```php
//...same as before ...

$serializer = new DeepCopySerializer(new JsonTransformer());
echo $serializer->serialize($post);
```

Following, there are some examples and its output, given the `$post` object as data to be Transformed.

### Array Transformer

- [`NilPortugues\Serializer\Transformer\ArrayTransformer`](https://github.com/nilportugues/serializer/blob/master/src/Transformer/ArrayTransformer.php)

```php
array(
  'postId' => 9,
  'title' => 'Hello World',
  'content' => 'Your first post',
  'author' => array(
       'userId' => 1,
       'name' => 'Post Author',
   ),
  'comments' => array(
          0 => array(
           'commentId' => 1000,
           'dates' => array(
              'created_at' => '2015-07-18T12:13:00+02:00',
              'accepted_at' => '2015-07-19T00:00:00+02:00',
            ),
           'comment' => 'Have no fear, sers, your king is safe.',
           'user' => array(
             'userId' => 2,
             'name' => 'Barristan Selmy',
            ),
          ),
      ),
);
```

### Flat Array Transformer

- [`NilPortugues\Serializer\Transformer\FlatArrayTransformer`](https://github.com/nilportugues/serializer/blob/master/src/Transformer/FlatArrayTransformer.php)

```php
array(
  'postId' => 9,
  'title' => 'Hello World',
  'content' => 'Your first post',
  'author.userId' => 1,
  'author.name' => 'Post Author',
  'comments.0.commentId' => 1000,
  'comments.0.dates.created_at' => '2015-07-18T12:13:00+02:00',
  'comments.0.dates.accepted_at' => '2015-07-19T00:00:00+02:00',
  'comments.0.comment' => 'Have no fear, sers, your king is safe.',
  'comments.0.user.userId' => 2,
  'comments.0.user.name' => 'Barristan Selmy',
);
```

### XML Transformer

- [`NilPortugues\Serializer\Transformer\XmlTransformer`](https://github.com/nilportugues/serializer/blob/master/src/Transformer/XmlTransformer.php)

```xml
<?xml version="1.0" encoding="UTF-8"?>
<data>
  <postId type="integer">9</postId>
  <title type="string">Hello World</title>
  <content type="string">Your first post</content>
  <author>
    <userId type="integer">1</userId>
    <name type="string">Post Author</name>
  </author>
  <comments>
    <sequential-item>
      <commentId type="integer">1000</commentId>
      <dates>
        <created_at type="string">2015-07-18T12:13:00+02:00</created_at>
        <accepted_at type="string">2015-07-19T00:00:00+02:00</accepted_at>
      </dates>
      <comment type="string">Have no fear, sers, your king is safe.</comment>
      <user>
        <userId type="integer">2</userId>
        <name type="string">Barristan Selmy</name>
      </user>
    </sequential-item>
  </comments>
</data>
```

### YAML Transformer

- [`NilPortugues\Serializer\Transformer\YamlTransformer`](https://github.com/nilportugues/serializer/blob/master/src/Transformer/YamlTransformer.php)

```yml
title: 'Hello World'
content: 'Your first post'
author:
    userId: 1
    name: 'Post Author'
comments:
    - { commentId: 1000, dates: { created_at: '2015-07-18T12:13:00+02:00', accepted_at: '2015-07-19T00:00:00+02:00' }, comment: 'Have no fear, sers, your king is safe.', user: { userId: 2, name: 'Barristan Selmy' } }
```

### Json Transformer

JsonTransformer comes in 2 flavours. For object to JSON transformation the following transformer should be used:

- [`NilPortugues\Serializer\Transformer\JsonTransformer`](https://github.com/nilportugues/serializer/blob/master/src/Transformer/JsonTransformer.php)

**Output**

```json
{
    "postId": 9,
    "title": "Hello World",
    "content": "Your first post",
    "author": {
        "userId": 1,
        "name": "Post Author"
    },
    "comments": [
        {
            "commentId": 1000,
            "dates": {
                "created_at": "2015-07-18T13:34:55+02:00",
                "accepted_at": "2015-07-18T14:09:55+02:00"
            },
            "comment": "Have no fear, sers, your king is safe.",
            "user": {
                "userId": 2,
                "name": "Barristan Selmy"
            }
        }
    ]
}
```

If your desired output is for **API consumption**, you may like to check out the JsonTransformer library, or require it using:

```json
$ composer require nilportugues/json
```

### JSend Transformer

JSend Transformer has been built to transform data into valid **JSend** specification resources.

Please check out the [JSend Transformer](https://github.com/nilportugues/jsend-transformer) or download it using:

```json
$ composer require nilportugues/jsend
```

### JSON API Transformer

JSON API Transformer has been built to transform data into valid **JSON API** specification resources.

Please check out the [JSON API Transformer](https://github.com/nilportugues/jsonapi-transformer) or download it using:

```json
$ composer require nilportugues/json-api
```

### HAL+JSON Transformer

HAL+JSON Transformer has been built for **HAL+JSON API creation**. Given an object and a series of mappings a valid HAL+JSON resource representation is given as output.

Please check out the [HAL+JSON API Transformer](https://github.com/nilportugues/hal-json-transformer) or download it using:

```json
$ composer require nilportugues/haljson
```

----

## Quality

To run the PHPUnit tests at the command line, go to the tests directory and issue `phpunit`.

This library attempts to comply with [PSR-2](http://www.php-fig.org/psr/psr-2/) and [PSR-4](http://www.php-fig.org/psr/psr-4/).

If you notice compliance oversights, please send a patch via pull request.

## Contribute

Contributions to the package are always welcome!

* Report any bugs or issues you find on the [issue tracker](https://github.com/nilportugues/serializer/issues/new).
* You can grab the source code at the package's [Git repository](https://github.com/nilportugues/serializer).

## Authors

* [Nil Portugués Calderó](http://nilportugues.com)
* [The Community Contributors](https://github.com/nilportugues/serializer/graphs/contributors)

## License
The code base is licensed under the MIT license.


text:
'use strict';

const globalHooks = require('../../../hooks');
const hooks       = require('feathers-hooks');
const auth        = require('feathers-authentication').hooks;

const restrictToRolesOptons = {
    roles      : ['systemAdmin'],
    fieldName  : 'role',
    idField    : '_id',
    ownerField : '_id',
    owner      : true
};

exports.before = {
    all    : [
        auth.verifyToken(),
        auth.populateUser(),
        auth.restrictToAuthenticated(),
        //auth.restrictToRoles(restrictToRolesOptons)
        //globalHooks.logHook()
    ],
    find   : [
    ],
    get    : [
    ],
    create : [
        auth.hashPassword()
    ],
    update : [
        auth.hashPassword()
    ],
    patch  : [
        auth.hashPassword()
    ],
    remove : [
    ]
};

exports.after = {
    all    : [
        hooks.remove('password')
    ],
    find   : [],
    get    : [],
    create : [],
    update : [],
    patch  : [],
    remove : []
};


text:
import Ember from 'ember';
import Mixin from 'ember-metal/mixin';

import { alias } from 'ember-computed';
import observer  from 'ember-metal/observer';

const { Binding, defineProperty } = Ember;

// TODO: Use Ember.Binding
export default Mixin.create({
  classNameBindings: ['valuesDiffer:different-value', 'valuesUnsaved:unsaved'],

  propertyPathDidChange: observer('propertyPath', 'propertyTarget', 'originPath',
  function() {
    let propertyPath = this.get('propertyPath');
    let targetPath   = this.get('propertyTarget');
    let originPath   = this.get('originPath');

    if (typeof propertyPath === 'string') {
      propertyPath = `object.${propertyPath}`;

      let differKey = `${propertyPath}IsDifferent`;
      let unsavedKey;

      if (originPath) {
        unsavedKey = `object.${originPath}IsChanged`;
      } else {
        unsavedKey = `${propertyPath}IsChanged`;
      }

      defineProperty(this, 'valuesDiffer', alias(differKey));
      defineProperty(this, 'valuesUnsaved', alias(unsavedKey));

      let propertyBinding = this.get('propertyBinding');

      if (propertyBinding != null &&
          typeof propertyBinding.disconnect === 'function') {
        propertyBinding.disconnect(this);
      }

      if (typeof propertyPath === 'string' && typeof targetPath === 'string') {
        propertyBinding = Binding.from(propertyPath).to(targetPath);
        propertyBinding.connect(this);
      }
    }
  }).on('init')
});


text:
package patterncomposite.impl;

import patterncomposite.MenuComponent;
import patterncomposite.iterators.NullIterator;

import java.util.Iterator;

/**
 * Project: PatternsBook
 * Date: 07.03.14
 * Time: 23:51
 *
 * @author Skurishin Vladislav
 */
public class MenuItem extends MenuComponent
{
    private String name;
    private String description;
    private boolean vegetarian;
    private double price;

    public MenuItem(String name,
                    String description,
                    boolean vegetarian,
                    double price)
    {
        this.name = name;
        this.description = description;
        this.vegetarian = vegetarian;
        this.price = price;
    }

    @Override
    public String getName()
    {
        return name;
    }

    @Override
    public String getDescription()
    {
        return description;
    }

    @Override
    public boolean isVegetarian()
    {
        return vegetarian;
    }

    @Override
    public double getPrice()
    {
        return price;
    }

    @Override
    public void print()
    {
        System.out.print(" " + getName());
        if (isVegetarian()) {
            System.out.print("(v)");
        }
        System.out.println(", " + getPrice());
        System.out.println("    -- " + getDescription());
    }

    @Override
    public Iterator<MenuComponent> createIterator()
    {
        return new NullIterator();
    }
}


text:
package com.websudos.phantom.zookeeper

import com.datastax.driver.core.Cluster
import com.websudos.phantom.connectors.KeySpaceBuilder
import com.twitter.finagle.exp.zookeeper.ZooKeeper
import com.twitter.finagle.exp.zookeeper.client.ZkClient
import com.twitter.conversions.time._
import com.twitter.util._

import scala.collection.JavaConverters._
import java.net.InetSocketAddress

/**
 * Performs the lookup of Cassandra nodes
 * in Zookeeper. Implemented as function
 * `Cluster.Builder => Cluster.Builder` so that it can
 * be easily plugged into a `KeySpaceBuilder`.
 */
class ZkContactPointLookup(
  host: String,
  port: Int,
  path: String = "/cassandra",
  timeout: Duration = 5.seconds) extends (Cluster.Builder => Cluster.Builder) {

  def apply(builder: Cluster.Builder): Cluster.Builder = {
    val ports = Await.result(retrieveContactPoints, timeout)
    builder.addContactPointsWithPorts(ports.asJava)
  }

  case class LookupException(msg: String) extends RuntimeException(msg) with NoStacktrace

  /**
   * Exposes the instance of the Finagle
   * Zookeeper API that is used to perform the lookup.
   */
  protected[this] lazy val zkClient: ZkClient = {
    val conn = s"$host:$port"
    val client = ZooKeeper.newRichClient(conn)
    Await.result(client.connect(), timeout)
    client
  }

  /**
   * Performs the actual lookup of Cassandra nodes
   * in Zookeeper.
   */
  protected[this] def retrieveContactPoints: Future[Seq[InetSocketAddress]] =
    zkClient.getData(path, watch = false) map {
      res => Try {
        parseContactPoints(new String(res.data))
      } getOrElse Seq.empty[InetSocketAddress]
    }

  /**
   * Parses the specified string into a sequence
   * of InetSocketAddress instances. The default
   * implementation expects a comma
   * separated list of host/port pairs separated by a colon.
   */
  @throws[LookupException]
  protected[this] def parseContactPoints(data: String): Seq[InetSocketAddress] = {
    data.split("\\s*,\\s*").map(_.split(":")).map {
      case Array(hostname, portDef) => new InetSocketAddress(hostname, portDef.toInt)
      case _ => throw LookupException(s"Could not parse contact points from $data")
    }.toSeq
  }

}

/**
 * Entry point for defining a keySpace based
 * on looking up Cassandra nodes in Zookeeper.
 *
 * The default Zookeeper path being used for the lookup
 * is `/cassandra` and the expected format is a comma
 * separated list of host/port pairs separated by a colon.
 */
object ZkContactPointLookup {

  /**
   * Zookeeper's default port.
   */
  val defaultPort = 2181

  /**
   * A keyspace builder based on looking up
   * contact points in Zookeeper running on the default
   * port on localhost.
   */
  lazy val local = apply(defaultPort)

  /**
   * A keyspace builder based on looking up
   * contact points in Zookeeper running on the specified
   * port on localhost.
   */
  def apply(port: Int): KeySpaceBuilder = apply("localhost", port)

  /**
   * A keyspace builder based on looking up
   * contact points in Zookeeper running on the specified
   * host and port.
   */
  def apply(host: String, port: Int): KeySpaceBuilder =
    new KeySpaceBuilder(new ZkContactPointLookup(host,port))

  /**
   * A keyspace builder based on looking up
   * contact points in Zookeeper running on the specified
   * host and port.
   *
   * @param host the host Zookeeper runs on
   * @param port the port Zookeeper runs on
   * @param path the Zookeeper path to use for looking up Cassandra nodes
   * @param timeout the timeout for performing the lookup
   */
  def apply(host: String, port: Int, path: String, timeout: Duration): KeySpaceBuilder =
    new KeySpaceBuilder(new ZkContactPointLookup(host, port, path, timeout))

}


text:
package loglang.misc;

/**
 * Created by skgchxngsxyz-osx on 15/10/05.
 */
public class FatalError extends Error {
    private static final long serialVersionUID = 8017043677701089665L;

    public FatalError(String message) {
        super(message);
    }
}


text:
(function() {

'use strict';

var WAGNER = WAGNER || {};

WAGNER.vertexShadersPath = 'lib/WagnerVertexShaders';
WAGNER.fragmentShadersPath = 'lib/WagnerFragmentShaders';
WAGNER.assetsPath = 'lib/WagnerAssets';

WAGNER.log = function() {
	//console.log( Array.prototype.slice.call( arguments ).join( ' ' ) );
};

WAGNER.Composer = function( renderer, settings ) {

	this.width = 1;
	this.height = 1;

	this.settings = settings || {};
	this.useRGBA = this.settings.useRGBA || false;

	this.renderer = renderer;
	this.copyPass = new WAGNER.CopyPass( this.settings );

	this.scene = new THREE.Scene();
	this.quad = new THREE.Mesh(
		new THREE.PlaneGeometry( 1, 1 ),
		this.defaultMaterial
	);
	this.scene.add( this.quad );
	this.camera = new THREE.OrthographicCamera( 1, 1, 1, 1, -10000, 10000 );

	this.front = new THREE.WebGLRenderTarget(1, 1, {
		minFilter: this.settings.minFilter !== undefined ? this.settings.minFilter : THREE.LinearFilter,
		magFilter: this.settings.magFilter !== undefined ? this.settings.magFilter : THREE.LinearFilter,
		wrapS: this.settings.wrapS !== undefined ? this.settings.wrapS : THREE.ClampToEdgeWrapping,
		wrapT: this.settings.wrapT !== undefined ? this.settings.wrapT : THREE.ClampToEdgeWrapping,
		format: this.useRGBA ? THREE.RGBAFormat : THREE.RGBFormat,
		type: this.settings.type !== undefined ? this.settings.type : THREE.UnsignedByteType,
		stencilBuffer: this.settings.stencilBuffer !== undefined ? this.settings.stencilBuffer : true
	});

	this.back = this.front.clone();

	this.startTime = Date.now();

	this.passes = {};

};

WAGNER.Composer.prototype.linkPass = function( id, pass ) {

	function WagnerLoadPassException( message ) {
		this.message = 'Pass "' + id + '" already loaded.';
		this.name = "WagnerLoadPassException";
		this.toString = function() {
			return this.message;
		};
	}

	if( this.passes[ id ] ) {
		throw new WagnerLoadPassException( id, pass );
	}

	this.passes[ id ] = pass;

};

WAGNER.Composer.prototype.swapBuffers = function() {

	this.output = this.write;
	this.input = this.read;

	var t = this.write;
	this.write = this.read;
	this.read = t;

};

WAGNER.Composer.prototype.render = function( scene, camera, keep, output ) {

	if( this.copyPass.isLoaded() ) {
		if( keep ) this.swapBuffers();
		this.renderer.render( scene, camera, output?output:this.write, true );
		if( !output ) this.swapBuffers();
	}

};

WAGNER.Composer.prototype.toScreen = function() {

	if( this.copyPass.isLoaded() ) {
		this.quad.material = this.copyPass.shader;
		this.quad.material.uniforms.tInput.value = this.read;
		this.quad.material.uniforms.resolution.value.set( this.width, this.height );
		this.renderer.render( this.scene, this.camera );
	}

};

WAGNER.Composer.prototype.toTexture = function( t ) {

	if( this.copyPass.isLoaded() ) {
		this.quad.material = this.copyPass.shader;
		this.quad.material.uniforms.tInput.value = this.read;
		this.renderer.render( this.scene, this.camera, t, true );
	}

};

WAGNER.Composer.prototype.pass = function( pass ) {

	if( typeof pass === 'string' ) {
		this.quad.material = this.passes[ pass ];
	}
	if( pass instanceof THREE.ShaderMaterial ) {
		this.quad.material = pass;
	}
	if( pass instanceof WAGNER.Pass ) {
		if( !pass.isLoaded() ) return;
		pass.run( this );
		return;
	}

	if( !pass.isSim ) this.quad.material.uniforms.tInput.value = this.read;

	this.quad.material.uniforms.resolution.value.set( this.width, this.height );
	this.quad.material.uniforms.time.value = 0.001 * ( Date.now() - this.startTime );
	this.renderer.render( this.scene, this.camera, this.write, false );
	this.swapBuffers();

};

WAGNER.Composer.prototype.reset = function() {

	this.read = this.front;
	this.write = this.back;

	this.output = this.write;
	this.input = this.read;

};

WAGNER.Composer.prototype.setSource = function( src ) {

	if( this.copyPass.isLoaded() ) {
		this.quad.material = this.copyPass.shader;
		this.quad.material.uniforms.tInput.value = src;
		this.renderer.render( this.scene, this.camera, this.write, true );
		this.swapBuffers();
	}

};

WAGNER.Composer.prototype.setSize = function( w, h ) {

	this.width = w;
	this.height = h;

	this.camera.projectionMatrix.makeOrthographic( w / - 2, w / 2, h / 2, h / - 2, this.camera.near, this.camera.far );
	this.quad.scale.set( w, h, 1 );

	var rt = this.front.clone();
	rt.width = w;
	rt.height = h;
	if( this.quad.material instanceof WAGNER.Pass ) this.quad.material.uniforms.tInput.value = rt;
	this.front = rt;

	rt = this.back.clone();
	rt.width = w;
	rt.height = h;
	this.back = rt;

};

WAGNER.Composer.prototype.defaultMaterial = new THREE.MeshBasicMaterial();

WAGNER.loadShader = function( file, callback ) {

	var oReq = new XMLHttpRequest();
	oReq.onload = function() {
		var content = oReq.responseText;
		callback( content );
	}.bind( this );
	oReq.onerror = function() {

		function WagnerLoadShaderException( f ) {
			this.message = 'Shader "' + f + '" couldn\'t be loaded.';
			this.name = "WagnerLoadShaderException";
			this.toString = function() {
				return this.message;
			};
		}
		throw new WagnerLoadShaderException( file );
	};
	oReq.onabort = function() {

		function WagnerLoadShaderException( f ) {
			this.message = 'Shader "' + f + '" load was aborted.';
			this.name = "WagnerLoadShaderException";
			this.toString = function() {
				return this.message;
			};
		}
		throw new WagnerLoadShaderException( file );
	};
	oReq.open( 'get', file, true );
	oReq.send();

};

WAGNER.processShader = function( vertexShaderCode, fragmentShaderCode ) {

	WAGNER.log( 'Processing Shader | Performing uniform Reflection...' );

	var regExp = /uniform\s+([^\s]+)\s+([^\s]+)\s*;/gi;
	var regExp2 = /uniform\s+([^\s]+)\s+([^\s]+)\s*\[\s*(\w+)\s*\]*\s*;/gi;

	var typesMap = {

		sampler2D: { type: 't', value: function() { return new THREE.Texture(); } },
		samplerCube: { type: 't', value: function() {} },

		bool:  { type: 'b', value: function() { return 0; } },
		int:   { type: 'i', value: function() { return 0; } },
		float: { type: 'f', value: function() { return 0; } },

		vec2: { type: 'v2', value: function() { return new THREE.Vector2(); } },
		vec3: { type: 'v3', value: function() { return new THREE.Vector3(); } },
		vec4: { type: 'v4', value: function() { return new THREE.Vector4(); } },

		bvec2: { type: 'v2', value: function() { return new THREE.Vector2(); } },
		bvec3: { type: 'v3', value: function() { return new THREE.Vector3(); } },
		bvec4: { type: 'v4', value: function() { return new THREE.Vector4(); } },

		ivec2: { type: 'v2', value: function() { return new THREE.Vector2(); } },
		ivec3: { type: 'v3', value: function() { return new THREE.Vector3(); } },
		ivec4: { type: 'v4', value: function() { return new THREE.Vector4(); } },

		mat2: { type: 'v2', value: function() { return new THREE.Matrix2(); } },
		mat3: { type: 'v3', value: function() { return new THREE.Matrix3(); } },
		mat4: { type: 'v4', value: function() { return new THREE.Matrix4(); } }

	};

	var arrayTypesMap = {
		float: { type: 'fv', value: function() { return []; } },
		vec3: { type: 'v3v', value: function() { return []; } }
	};

	var matches;
	var uniforms = {
		resolution: { type: 'v2', value: new THREE.Vector2( 1, 1 ), default: true },
		time: { type: 'f', value: Date.now(), default: true },
		tInput: { type: 't', value: new THREE.Texture(), default: true }
	};

  var uniformType, uniformName, arraySize;

	while( ( matches = regExp.exec( fragmentShaderCode ) ) !== null) {
		if( matches.index === regExp.lastIndex) {
			regExp.lastIndex++;
		}
		uniformType = matches[ 1 ];
		uniformName = matches[ 2 ];
		WAGNER.log( '  > SINGLE', uniformType, uniformName );
		uniforms[ uniformName ] = {
			type: typesMap[ uniformType ].type,
			value: typesMap[ uniformType ].value()
		};
	}

	while( ( matches = regExp2.exec( fragmentShaderCode ) ) !== null) {
		if( matches.index === regExp.lastIndex) {
			regExp.lastIndex++;
		}
		uniformType = matches[ 1 ];
		uniformName = matches[ 2 ];
		arraySize = matches[ 3 ];
		WAGNER.log( '  > ARRAY', arraySize, uniformType, uniformName );
		uniforms[ uniformName ] = {
			type: arrayTypesMap[ uniformType ].type,
			value: arrayTypesMap[ uniformType ].value()
		};
	}

	WAGNER.log( 'Uniform reflection completed. Compiling...' );

	var shader = new THREE.ShaderMaterial( {
		uniforms: uniforms,
		vertexShader: vertexShaderCode,
		fragmentShader: fragmentShaderCode,
		shading: THREE.FlatShading,
		depthWrite: false,
		depthTest: false,
		transparent: true
	} );

	WAGNER.log( 'Compiled' );

	return shader;

};

WAGNER.Pass = function() {

	WAGNER.log( 'Pass constructor' );
	this.shader = null;
	this.loaded = null;
	this.params = {};
	this.isSim = false;

};

WAGNER.Pass.prototype.loadShader = function( id, c ) {

	var self = this;
	var vs = 'varying vec2 vUv; void main() { vUv = uv; gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 ); }';
	WAGNER.loadShader( WAGNER.fragmentShadersPath + '/' + id, function( fs ) {
		self.shader = WAGNER.processShader( vs, fs );
		//self.mapUniforms( self.shader.uniforms );
		if( c ) c.apply( self );
	} );

};

WAGNER.Pass.prototype.mapUniforms = function( uniforms ) {

	var params = this.params;

	for( var j in uniforms ) {
		if( !uniforms[ j ].default ) {
			(function( id ) {
				Object.defineProperty( params, id, {
					get : function(){ return uniforms[ id ].value; },
					set : function( v ){ uniforms[ id ].value = v; },
					configurable : false
				} );
			})( j );
		}
	}

};

WAGNER.Pass.prototype.run = function( c ) {

	//WAGNER.log( 'Pass run' );
	c.pass( this.shader );

};

WAGNER.Pass.prototype.isLoaded = function() {

	if( this.loaded === null ) {
		if( this.shader instanceof THREE.ShaderMaterial ) {
			this.loaded = true;
		}
	} else {
		return this.loaded;
	}

};

WAGNER.Pass.prototype.getOfflineTexture = function( w, h, useRGBA ){

	var rtTexture = new THREE.WebGLRenderTarget( w, h, {
		minFilter: THREE.LinearFilter,
		magFilter: THREE.LinearFilter,
		format: useRGBA?THREE.RGBAFormat:THREE.RGBFormat
	} );

	return rtTexture;

};

WAGNER.CopyPass = function() {

	WAGNER.Pass.call( this );
	WAGNER.log( 'CopyPass constructor' );
	this.loadShader( 'copy-fs.glsl' );

};

WAGNER.CopyPass.prototype = Object.create( WAGNER.Pass.prototype );

WAGNER.GenericPass = function( fragmentShaderSource, c ) {

	WAGNER.Pass.call( this );
	var self = this;
	WAGNER.loadShader( WAGNER.vertexShadersPath + '/orto-vs.glsl', function( vs ) {
		WAGNER.loadShader( fragmentShaderSource, function( fs ) {
			self.shader = WAGNER.processShader( vs, fs );
			if( c ) c.apply( self );
		} );
	} );

}

WAGNER.GenericPass.prototype = Object.create( WAGNER.Pass.prototype );

window.WAGNER = WAGNER;
})();


text:
<!DOCTYPE html>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
  <title>How to apply the MVVM pattern in reproducible research? - @haozhu233</title>
  <meta name="renderer" content="webkit" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>

<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />

<meta name="theme-color" content="#f8f5ec" />
<meta name="msapplication-navbutton-color" content="#f8f5ec">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="#f8f5ec">

<meta name="author" content="Hao Zhu" />
  <meta name="description" content="The penalty for a mistake on a simple structure is only a little time and maybe some embarrassment.
More Complicated structures require more careful planning.
&ndash; Steve McConnell in Code Complete 2
 If you are using Rmarkdown just to build a simple instruction or a blog post, you don&rsquo;t need to worry about your code structure at all. Mistakes in this kind of small projects are easy to be fixed." />

  <meta name="keywords" content="R, machine learning, Arduino, React Native" />

<meta name="generator" content="Hugo 0.53" />

<link rel="canonical" href="/post/2015-04-15-mvvm-in-reproducible-research/" />

<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
<link rel="icon" href="/favicon.ico" />
<link rel="manifest" href="/manifest.json">
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#5bbad5">

<link href="/dist/even.min.css?v=2.7.2" rel="stylesheet">
<link href="/lib/fancybox/jquery.fancybox-3.1.20.min.css" rel="stylesheet">

<meta property="og:title" content="How to apply the MVVM pattern in reproducible research?" />
<meta property="og:description" content="The penalty for a mistake on a simple structure is only a little time and maybe some embarrassment.
More Complicated structures require more careful planning.
– Steve McConnell in Code Complete 2
 If you are using Rmarkdown just to build a simple instruction or a blog post, you don’t need to worry about your code structure at all. Mistakes in this kind of small projects are easy to be fixed." />
<meta property="og:type" content="article" />
<meta property="og:url" content="/post/2015-04-15-mvvm-in-reproducible-research/" /><meta property="article:published_time" content="2015-04-15T00:00:00+00:00"/>
<meta property="article:modified_time" content="2015-04-15T00:00:00+00:00"/>

<meta itemprop="name" content="How to apply the MVVM pattern in reproducible research?">
<meta itemprop="description" content="The penalty for a mistake on a simple structure is only a little time and maybe some embarrassment.
More Complicated structures require more careful planning.
– Steve McConnell in Code Complete 2
 If you are using Rmarkdown just to build a simple instruction or a blog post, you don’t need to worry about your code structure at all. Mistakes in this kind of small projects are easy to be fixed.">

<meta itemprop="datePublished" content="2015-04-15T00:00:00+00:00" />
<meta itemprop="dateModified" content="2015-04-15T00:00:00+00:00" />
<meta itemprop="wordCount" content="842">

<meta itemprop="keywords" content="" />
<meta name="twitter:card" content="summary"/>
<meta name="twitter:title" content="How to apply the MVVM pattern in reproducible research?"/>
<meta name="twitter:description" content="The penalty for a mistake on a simple structure is only a little time and maybe some embarrassment.
More Complicated structures require more careful planning.
– Steve McConnell in Code Complete 2
 If you are using Rmarkdown just to build a simple instruction or a blog post, you don’t need to worry about your code structure at all. Mistakes in this kind of small projects are easy to be fixed."/>

    @haozhu233

Home

Archives

Tags

Categories

About

  @haozhu233

        Home

        Archives

        Tags

        Categories

        About

How to apply the MVVM pattern in reproducible research?

         2015-04-15

Contents

What is MVVM?

How can we use MVVM design pattern in reproducible research?

The penalty for a mistake on a simple structure is only a little time and maybe some embarrassment.

More Complicated structures require more careful planning.

– Steve McConnell in Code Complete 2

If you are using Rmarkdown just to build a simple instruction or a blog post, you don’t need to worry about your code structure at all. Mistakes in this kind of small projects are easy to be fixed. However, if you are asking Rmarkdownto generate a 50-page-long paper with 10 tables and 5 figures, you would better to think of a good way to organize your codes because you don’t want to get into a case in which you have to make edits to 200 different places throughout your markdown document in order to fix a small mistake 200 times. This kind of code revision is time-consuming and you might also get a chance to make some stupid mistakes that can only increase your work load and make you feel worse. Having a good software architectural design is also very meaningful if you are collaborating with someone else. A clean code structure plays an important role in helping other people understand your codes.

People in the field of software engineering developped many design patterns for software architecture. I don’t want to pretend I know a lot about them but as far as I know, I believe the most suitable design pattern for a long and complicated reproducible Rmarkdown report is something called Model/View/ViewModel (MVVM)

What is MVVM?

The concept of MVVM was first announced by John Gossman in his blog in 2005. It is a variation of Model/View/Controller (MVC), which is one of the most important designs in software architecture. The MVVM design is widely adopted in today’s website design, where the Model is the database, the View is the webpage and the View Model is the connection part that packs up data from the database and pass them to the View part. I’ll provide an example of using these concepts in reproducible research in the next section but you can alway go to John’s blog, which explained these concepts in a much better way,
for more information.

Also, I really like Ryan Nystrom’s metaphor about MVC on Quora, even though I believe his painting metaphor fits the concepts of MVVM better. In his post, he said,

> Paints are the model. They have unique and similar properties to other paints. You can mix them or use them as pure as possible.

The painter is the controller. The painter performs the task using the paint and easel. The painter takes the paint from the palette and decides how to apply it to the easel.

The easel is the view. It is agnostic of the painter and paint and doesn’t care how it will be used. It does, however, have qualities like size and texture that effect the outcome of the painting.

How can we use MVVM design pattern in reproducible research?

Suppose we are trying to write a reproducible report for a randomized clinical trial. This trial has 2 visits and 5 clinical measuring instruments(A1, A2,…, A5). The results for each instrument are stored in separated files and the randomization information is stored in a file called “random.txt”. We are supposed to make 2 tables in the final report.

Step 1. Model (A1.R, A2.R,…, A5.R files)
In this step, you combine the randomization file with the data file for each instrument, clean the data, and do some necessary reformatting to make sure you can have tidy data available for all the tests. You may want to export these tidy data if you have the need to share the cleaned database with someone else.

Step 2. View Models (table1.R, table2.R, figure1.R files)
In this step, you are supposed to generate tables and figures, which are mostly ready to be printed on the paper. However, you don’t need to worry about the table/figure formatting (Title, footnotes…, etc.) here. Those accessaries can be easily added on during the 3rd step. Here, for example, if table 1 need all the baseline visit data from test A1, A2 and A3, you will need to source A1.R, A2.R and A3.R, merge/join these tidy data for baseline, and do appropriate analysis to generate the table. I will suggest you to put all of the files in this step in a sub-folder with the name (or nickname) of the paper, for example, “primary analysis”.

Step 3. View (primary.analysis.rmd file)
In this step, you should source all the tables/figures you generated in Step2 at the very beginning of your .rmd file. Then you can go ahead to write the report like you usually do and print the tables/figures when necessary.

This is just an example showing how I would adopt the MVVM design into my code structure. Different people may have different understandings to the concepts of this design. Also, in different cases with differnt scales, you might want to modify this structure as you need, as long as the ultimate goal of constructing a maintainable project is achieved.

    Author
    Hao Zhu

    LastMod
    2015-04-15

    License
    CC BY-NC-ND 4.0

            "Build" a reproducible report, instead of "writing" one
            Prev

            REDCap API and R Shiny
            Next

    Powered by Hugo

  |

    Theme -
    Even

    ©

      2018 -
    2019

    Hao Zhu


text:
# [2296\. Design a Text Editor](https://leetcode.com/problems/design-a-text-editor/)

## Description

Difficulty: **Hard**

Related Topics:

Design a text editor with a cursor that can do the following:

*   **Add** text to where the cursor is.
*   **Delete** text from where the cursor is (simulating the backspace key).
*   **Move** the cursor either left or right.

When deleting text, only characters to the left of the cursor will be deleted. The cursor will also remain within the actual text and cannot be moved beyond it. More formally, we have that `0 <= cursor.position <= currentText.length` always holds.

Implement the `TextEditor` class:

*   `TextEditor()` Initializes the object with empty text.
*   `void addText(string text)` Appends `text` to where the cursor is. The cursor ends to the right of `text`.
*   `int deleteText(int k)` Deletes `k` characters to the left of the cursor. Returns the number of characters actually deleted.
*   `string cursorLeft(int k)` Moves the cursor to the left `k` times. Returns the last `min(10, len)` characters to the left of the cursor, where `len` is the number of characters to the left of the cursor.
*   `string cursorRight(int k)` Moves the cursor to the right `k` times. Returns the last `min(10, len)` characters to the left of the cursor, where `len` is the number of characters to the left of the cursor.

**Example 1:**

```
Input
["TextEditor", "addText", "deleteText", "addText", "cursorRight", "cursorLeft", "deleteText", "cursorLeft", "cursorRight"]
[[], ["leetcode"], [4], ["practice"], [3], [8], [10], [2], [6]]
Output
[null, null, 4, null, "etpractice", "leet", 4, "", "practi"]

Explanation
TextEditor textEditor = new TextEditor(); // The current text is "|". (The '|' character represents the cursor)
textEditor.addText("leetcode"); // The current text is "leetcode|".
textEditor.deleteText(4); // return 4
                          // The current text is "leet|".
                          // 4 characters were deleted.
textEditor.addText("practice"); // The current text is "leetpractice|".
textEditor.cursorRight(3); // return "etpractice"
                           // The current text is "leetpractice|".
                           // The cursor cannot be moved beyond the actual text and thus did not move.
                           // "etpractice" is the last 10 characters to the left of the cursor.
textEditor.cursorLeft(8); // return "leet"
                          // The current text is "leet|practice".
                          // "leet" is the last min(10, 4) = 4 characters to the left of the cursor.
textEditor.deleteText(10); // return 4
                           // The current text is "|practice".
                           // Only 4 characters were deleted.
textEditor.cursorLeft(2); // return ""
                          // The current text is "|practice".
                          // The cursor cannot be moved beyond the actual text and thus did not move.
                          // "" is the last min(10, 0) = 0 characters to the left of the cursor.
textEditor.cursorRight(6); // return "practi"
                           // The current text is "practi|ce".
                           // "practi" is the last min(10, 6) = 6 characters to the left of the cursor.
```

**Constraints:**

*   `1 <= text.length, k <= 40`
*   `text` consists of lowercase English letters.
*   At most 2 * 104 calls **in total** will be made to `addText`, `deleteText`, `cursorLeft` and `cursorRight`.

## Solution

Language: **JavaScript**

```javascript

var TextEditor = function() {
  this.prev = ''
  this.next = ''
};

/**
 * @param {string} text
 * @return {void}
 */
TextEditor.prototype.addText = function(text) {
  this.prev += text
};

/**
 * @param {number} k
 * @return {number}
 */
TextEditor.prototype.deleteText = function(k) {
  const start = Math.max(0, this.prev.length - k)
  const count = this.prev.length - start
  this.prev = this.prev.slice(0, start)
  return count
};

TextEditor.prototype.getLeftText = function() {
  const start = Math.max(0, this.prev.length - 10)
  return this.prev.slice(start, this.c)
};

/**
 * @param {number} k
 * @return {string}
 */
TextEditor.prototype.cursorLeft = function(k) {
  const start = Math.max(0, this.prev.length - k)
  this.next = this.prev.slice(start) + this.next
  this.prev = this.prev.slice(0, start)
  return this.getLeftText()
};

/**
 * @param {number} k
 * @return {string}
 */
TextEditor.prototype.cursorRight = function(k) {
  const end = Math.min(this.next.length, k)
  this.prev += this.next.slice(0, end)
  this.next = this.next.slice(end)
  return this.getLeftText()
};

/**
 * Your TextEditor object will be instantiated and called as such:
 * var obj = new TextEditor()
 * obj.addText(text)
 * var param_2 = obj.deleteText(k)
 * var param_3 = obj.cursorLeft(k)
 * var param_4 = obj.cursorRight(k)
 */
```


text:
<?xml version="1.0" encoding="UTF-8"?>

<web-app version="3.0" xmlns="http://java.sun.com/xml/ns/javaee"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">

	<display-name>sample-server-app</display-name>

	<context-param>
		<param-name>contextClass</param-name>
		<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
	</context-param>

	<context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>io.github.tomacla.server.app.conf.SpringBootstrap</param-value>
	</context-param>

	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>

	<servlet>
		<servlet-name>jersey</servlet-name>
		<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
		<init-param>
			<param-name>javax.ws.rs.Application</param-name>
			<param-value>io.github.tomacla.server.app.conf.JerseyBootstrap</param-value>
		</init-param>
		<load-on-startup>1</load-on-startup>
	</servlet>

	<servlet-mapping>
		<servlet-name>jersey</servlet-name>
		<url-pattern>/*</url-pattern>
	</servlet-mapping>

	<filter>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>

	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

	<filter>
		<filter-name>springSecurityFilterChain</filter-name>
		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
	</filter>

	<filter-mapping>
		<filter-name>springSecurityFilterChain</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>

</web-app>


text:
<?php echo CHtml::beginForm(); ?>

<?php echo CHtml::label('Input your Code','code'); ?>
<?php echo CHtml::textField('code'); ?>

<?php echo CHtml::label("ID",'id'); ?>
<?php echo CHtml::textField('id'); ?>

<?php echo CHtml::submitButton(); ?>

<?php echo CHtml::endForm(); ?>


text:
function mibModelShowCheck_Callback(obj)
% function mibModelShowCheck_Callback(obj)
% callback to the mibGUI.handles.mibModelShowCheck to toggle the Model layer on/off
%
% Parameters:
%
% Return values
%

% Copyright (C) 20.11.2016, Ilya Belevich, University of Helsinki (ilya.belevich @ helsinki.fi)
% part of Microscopy Image Browser, http:\\mib.helsinki.fi
% This program is free software; you can redistribute it and/or
% modify it under the terms of the GNU General Public License
% as published by the Free Software Foundation; either version 2
% of the License, or (at your option) any later version.
%
% Updates
%

if obj.mibModel.I{obj.mibModel.Id}.modelExist == 0
    obj.mibView.handles.mibModelShowCheck.Value = 0;
    obj.mibModel.mibModelShowCheck = 0;
    return;
end
obj.mibModel.mibModelShowCheck = obj.mibView.handles.mibModelShowCheck.Value;
obj.mibModel.I{obj.mibModel.Id}.generateModelColors();
obj.plotImage(0);
end


text:
/*
 * Do not modify this file. This file is generated from the iam-2010-05-08.normal.json service model.
 */
using System;
using System.Collections.Generic;
using System.Xml.Serialization;
using System.Text;
using System.IO;

using Amazon.Runtime;
using Amazon.Runtime.Internal;

namespace Amazon.IdentityManagement.Model
{
    ///
    /// Container for the parameters to the ListAttachedGroupPolicies operation.
    /// Lists all managed policies that are attached to the specified group.
    ///
    ///
    /// <para>
    /// A group can also have inline policies embedded with it. To list the inline policies
    /// for a group, use the ListGroupPolicies API. For information about policies,
    /// refer to Managed
    /// Policies and Inline Policies in the Using IAM guide.
    /// </para>
    ///
    /// <para>
    /// You can paginate the results using the MaxItems and Marker
    /// parameters. You can use the PathPrefix parameter to limit the list of
    /// policies to only those matching the specified path prefix. If there are no policies
    /// attached to the specified group (or none that match the specified path prefix), the
    /// action returns an empty list.
    /// </para>
    ///
    public partial class ListAttachedGroupPoliciesRequest : AmazonIdentityManagementServiceRequest
    {
        private string _groupName;
        private string _marker;
        private int? _maxItems;
        private string _pathPrefix;

        ///
        /// Gets and sets the property GroupName.
        /// <para>
        /// The name (friendly name, not ARN) of the group to list attached policies for.
        /// </para>
        ///
        public string GroupName
        {
            get { return this._groupName; }
            set { this._groupName = value; }
        }

        // Check to see if GroupName property is set
        internal bool IsSetGroupName()
        {
            return this._groupName != null;
        }

        ///
        /// Gets and sets the property Marker.
        /// <para>
        /// Use this parameter only when paginating results and only after you receive a response
        /// indicating that the results are truncated. Set it to the value of the Marker
        /// element in the response you received to inform the next call about where to start.
        /// </para>
        ///
        public string Marker
        {
            get { return this._marker; }
            set { this._marker = value; }
        }

        // Check to see if Marker property is set
        internal bool IsSetMarker()
        {
            return this._marker != null;
        }

        ///
        /// Gets and sets the property MaxItems.
        /// <para>
        /// Use this only when paginating results to indicate the maximum number of items you
        /// want in the response. If there are additional items beyond the maximum you specify,
        /// the IsTruncated response element is true.
        /// </para>
        ///
        /// <para>
        /// This parameter is optional. If you do not include it, it defaults to 100.
        /// </para>
        ///
        public int MaxItems
        {
            get { return this._maxItems.GetValueOrDefault(); }
            set { this._maxItems = value; }
        }

        // Check to see if MaxItems property is set
        internal bool IsSetMaxItems()
        {
            return this._maxItems.HasValue;
        }

        ///
        /// Gets and sets the property PathPrefix.
        /// <para>
        /// The path prefix for filtering the results. This parameter is optional. If it is not
        /// included, it defaults to a slash (/), listing all policies.
        /// </para>
        ///
        public string PathPrefix
        {
            get { return this._pathPrefix; }
            set { this._pathPrefix = value; }
        }

        // Check to see if PathPrefix property is set
        internal bool IsSetPathPrefix()
        {
            return this._pathPrefix != null;
        }

    }
}


text:
namespace ap
{
    /// Type alias for a buffer of samples
    /// Sample values are double to avoid precision issues
    using SampleBuf = std::vector<double>;

    /// TableGenerator interface class
    /// create a concrete table generator
    /// by implementing the generate() method in your subclass
    class TableGenerator
    {
        public:
            // Use default ctors/dtor as this class simply wraps a
            // generator method

            /// Implement this method in your table generator
            /// @precondition table_buffer is set to the size of the table
            ///     to generate
            /// @param table_buffer SampleBuf to place the samples
            virtual void generate(SampleBuf& table_buffer) = 0;
    };
}
#endif