text:
var express = require("express");
var bodyParser = require("body-parser");
var config = require('./config');
var pg = require('pg');
var mysql = require('mysql');
var app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
app.use(express.static(__dirname));
// var connection = mysql.createConnection({
//   host     : 'adamandjeff.cam975huvdwe.us-west-2.rds.amazonaws.com',
//   user     : 'aandj',
//   password : 'adamandjeff',
//   database : 'mysqlpractice'
// });

// var conString = process.env.ELEPHANTSQL_URL || "postgres://awdtqouh:" + pgconfig.pgkey + "@pellefant.db.elephantsql.com:5432/awdtqouh";
///### INSTANCE WITH CONNECT AND END####///
// var client = new pg.Client(conString);
// client.connect(function(err) {
//   if(err) {
//     return console.error('could not connect to postgres', err);
//   }
// client.query(), function(err, result) {
//   if(err) {
//     return console.error('error running query', err);
//   }
//   console.log(result);
//   //output: Tue Jan 15 2013 19:12:47 GMT-600 (CST)
//   client.end();
//   });
// });

app.get('/users', function (req, res) {
//   connection.connect(function (err) {
//   if (err) {
//     console.error('error connecting: ' + err.stack);
//     return;
//   }
// });
var connection = mysql.createConnection(config);

  connection.query('select * from site_user', function(err, rows, fields) {
    if (err) throw err;

    console.log('The result is: ', rows);
    var data = rows;
    console.log(data);
      var mapped = data.map(function (element, index) {
        return {id: element.id, username: element.name};
      });

    res.send(mapped);
  });

  connection.end();
});
//****reference for orchestrate for app.get(/users)***//
// db.list('users')
// .then(function (result) {
//   var data = result.body.results;
//   var mapped = data.map(function (element, index) {
//     return {id: element.path.key, username: element.value.username};
//   });
//   res.send(mapped);
// });

app.get('/userTasks/:username', function (req, res) {
  var username = req.params.username;
  var id;
  var queryResults;

  var connection = mysql.createConnection(config);

    connection.query('select * from site_user where name = \'' + username + '\'', function(err, result) {
      if(err) {
        return console.error('error running query1 of get', err);
      }
      // console.log("query1 result.rows: ", result);

      id = result[0].id;
      // console.log('the initial query found an id of: ', id);

        connection.query('create temporary table tempTable (select task.id, task.title, task.description, site_user.name as creator, task.assignee, task.status from task left join site_user on task.creator = site_user.id)', function(err, result) {
          if(err) {
            return console.error('error running query3 of get', err);
          }
          // console.log("query2 result.rows: ", result);

          connection.query('create temporary table tempTable2 (select tempTable.id, tempTable.title, tempTable.description, tempTable.creator, site_user.name as assignee, tempTable.status from tempTable left join site_user on tempTable.assignee = site_user.id)', function(err, result) {
            if(err) {
              return console.error('error running query4 of get', err);
            }
            // console.log("query3 result.rows: ", result);

            connection.query('drop temporary table tempTable', function(err, result) {
              if(err) {
                return console.error('error running query5 of get', err);
              }
              // console.log("query4 result.rows: ", result);

              connection.query('select * from tempTable2 where (creator = \'' + username + '\' or assignee = \'' + username + '\')', function(err, result) {
                if(err) {
                  return console.error('error running query6 of get', err);
                }
                // console.log("query5 result.rows: ", result);

                queryResults = result;

                connection.query('drop table tempTable2', function(err, result) {
                  if(err) {
                    return console.error('error running query7 of get', err);
                  }
                  // console.log("query6 result.rows: ", result);

                  // connection.end();

                  var mapped = queryResults.map(function (element, index) {
                    return {id: element.id, title: element.title, description: element.description, creator: element.creator, assignee: element.assignee, status: element.status};
                  });

                // console.log("mapped is: ", mapped);
                res.send(mapped);
                connection.end();
              });
            });
          });
        });
      });
    });
  });
// });
//***REFERENCE TO ORCHESTRATE FOR APP.GET/userTasks*****//
// db.search('tasks', 'value.assignee:'+ username +' OR value.creator:' +username)
// .then(function (result) {
//   var data = result.body.results;
//   var mapped = data.map(function (element, index) {
//     return {id: element.path.key, title: element.value.title, description: element.value.description, creator: element.value.creator, assignee: element.value.assignee, status: element.value.status};
//   });
//   res.send(mapped);
// })
// .fail(function (err) {});

app.get('/unassignedTasks/:username', function (req, res) {
  console.log("/unassignedTasks/:username GET route initiated");
  var username = req.params.username;
  var id;
  var queryResults;

  var connection = mysql.createConnection(config);

    connection.query("select task.id, task.title, task.description, site_user.name as creator, '' as assignee, task.status from task left join site_user on task.creator = site_user.id where (task.status = 'Unassigned' and site_user.name <> \'" + username + "\')", function(err, result) {
      if(err) {
        return console.error('error running query1 of get', err);
      }
      console.log("query1 result.rows: ", result);

      queryResults = result;
      // done();

      var mapped = queryResults.map(function (element, index) {
        return {id: element.id, title: element.title, description: element.description, creator: element.creator, assignee: element.assignee, status: element.status};
      });
      res.send(mapped);
      connection.end();
    });
  });
// });
///****** PREVIOUS VERSION USING ORCHESTRATE ******///
// app.get('/unassignedTasks/:username', function (req, res) {
//   console.log("/unassignedTasks/:username GET route initiated");
//   var username = req.params.username;
//   db.search('tasks', 'value.status: "Unassigned" AND value.creator: (NOT ' + username + ')')
//   .then(function (result) {
//     console.log("db.search successful");
//     var data = result.body.results;
//     var mapped = data.map(function (element, index) {
//       return {id: element.path.key, title: element.value.title, description: element.value.description, creator: element.value.creator, assignee: element.value.assignee, status: element.value.status};
//     });
//     res.send(mapped);
//   })
//   .fail(function (err) {console.log("db.search not successful");});
// });

app.post('/users', function (req, res) {
  var id;

  var connection = mysql.createConnection(config);

    connection.query('insert into site_user (name) values (\'' + req.body.username + '\')', function(err, result) {
      if(err) {
        return console.error('error running query1 of get', err);
      }
      console.log("query1 result.rows: ", result);

      connection.query('select max(id) from site_user', function(err, result) {
        if(err) {
          return console.error('error running query2 of get', err);
        }
        console.log("query2 result.rows: ", result);

        id = result[0].max;
        // done();

        res.send({id: id});
        connection.end();
      });
    });
});
///****** PREVIOUS VERSION USING ORCHESTRATE ******///
//   db.list('users')
//   .then(function(result) {
//     id = result.body.count + 1;
//     // console.log(id);
//     db.put('users', id, {"username" : req.body.username})
//     .then(function(result) {
//       // console.log(req.body.username);
//       res.send({id: id});
//     });
//   });
// });

app.put('/tasks/:id', function (req, res) {
  console.log("userTasks PUT initiated");
  var id = req.params.id;
  var queryResults;

  var connection = mysql.createConnection(config);

    if(req.body.assignee !== '') {
      connection.query('select id from site_user where name = \'' + req.body.assignee +'\'', function(err, result) {
        if(err) {
          return console.error('error running query1 of get', err);
        }
        console.log("query1 result.rows: ", result);

        queryResults = result;

        connection.query('update task set assignee = \'' + queryResults[0].id + '\', status = \'' + req.body.status + '\' where id = \'' + id + '\'', function(err, result) {
          if(err) {
            return console.error('error running query2 of get', err);
          }
          console.log("query2 result.rows: ", result);

          connection.end();
          res.send({id: id});
        });
      });
    }
    else {
      connection.query('update task set assignee = null, status = \'' + req.body.status + '\' where id = \'' + id + '\'', function(err, result) {
        if(err) {
          return console.error('error running query1 of get', err);
        }
        console.log("query1 result.rows: ", result);

        connection.end();

        res.send({id: id});
      });
    }
});
///****** PREVIOUS VERSION USING ORCHESTRATE ******///
// db.put('tasks', id, {"title" : req.body.title, "description" : req.body.description, "creator" : req.body.creator, "assignee" : req.body.assignee, "status": req.body.status})
// .then(function(result) {
//   console.log("task update works");
//   res.send({id: id});
// });

///****** PREVIOUS VERSION USING ORCHESTRATE ******///
app.post('/tasks', function (req, res) {   // was previously set to /userTasks/:username
  console.log("userTasks POST initiated");
  var id;
  var queryResultsCreator;
  var queryResultsAssignee;

  var connection = mysql.createConnection(config);

    connection.query('select id from site_user where name = \'' + req.body.creator + '\'', function(err, result) {
      if(err) {
        return console.error('error running query1 of get', err);
      }
      console.log("query1 result.rows: ", result);

      queryResultsCreator = result;

      if(req.body.assignee !== '') {
        connection.query('select id from site_user where name = \'' + req.body.assignee + '\'', function(err, result) {
          if(err) {
            return console.error('error running query2 of get', err);
          }
          console.log("query2 result.rows: ", result);

          queryResultsAssignee = result;

          connection.query('insert into task (title, description, creator, assignee, status) values (\'' + req.body.title + '\', \'' + req.body.description + '\', \'' + queryResultsCreator[0].id + '\', \'' + queryResultsAssignee[0].id + '\', \'' + req.body.status + '\')', function(err, result) {
            if(err) {
              return console.error('error running query3 of get', err);
            }
            console.log("query3 result.rows: ", result);

            connection.query('select max(id) from task', function(err, result) {
              if(err) {
                return console.error('error running query4 of get', err);
              }
              console.log("query4 result.rows: ", result);

              id = result[0].max;
              connection.end();

              res.send({id: id});
            });
          });
        });
      }
      else {
        connection.query('insert into task (title, description, creator, assignee, status) values (\'' + req.body.title + '\', \'' + req.body.description + '\', \'' + queryResultsCreator[0].id + '\', null, \'' + req.body.status + '\')', function(err, result) {
          if(err) {
            return console.error('error running query2 of get', err);
          }
          console.log("query2 result.rows: ", result);

          queryResultsAssignee = result;

          connection.query('select max(id) from task', function(err, result) {
            if(err) {
              return console.error('error running query3 of get', err);
            }
            console.log("query3 result.rows: ", result);

            id = result[0].max;
            connection.end();

            res.send({id: id});
          });
        });
      }
    });
});
///****** PREVIOUS VERSION USING ORCHESTRATE ******/// /// BELIEVED TO BE REDUNDANT ///
// app.put('/tasks/:id', function (req, res) {
//   console.log("unassignedTasks PUT initiated");
//   var id = req.params.id;
//   db.put('tasks', id, {"title" : req.body.title, "description" : req.body.description, "creator" : req.body.creator, "assignee" : req.body.assignee, "status": req.body.status})
//   .then(function(result) {
//     console.log("task update works");
//     res.send({id: id});
//   });
// });

///****** PREVIOUS VERSION USING ORCHESTRATE ******/// /// BELIEVED TO BE REDUNDANT ///
// app.post('/unassignedTasks', function (req, res) {
//   console.log("unassignedTasks POST initiated");
//   var id;
//   db.list('tasks')
//   .then(function(result) {
//     id = result.body.count + 1;
//     db.put('tasks', id, {"title" : req.body.title, "description" : req.body.description, "creator" : req.body.creator, "assignee" : req.body.assignee, "status": req.body.status})
//     .then(function(result) {
//       console.log("task create works");
//       res.send({id: id});
//     });
//   });
// });

app.listen(3000, function () {
  console.log("Server is running...........");
});


text:
name: UseESLintOnTheCommandLine
last_update: 2016-02-09
 .left-column[
  ### Command Line ESLint

No manual input required here.

.footnote[.red.bold[] [
Table of Contents](./toc.html)

   Last update

    {{ last_update  }}
]
]
.right-column[
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ - o 0 o - ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

#### Use ESLint on the Command Line

We installed command line ESLint in the first part, and will have it for Sublime Text momentarily.  Both have advantages: editor based linting helps ensure code quality as we type, while command line linting catches quality issues during continuous deployment.  A command line result looks like this :
```ruby
/home/yourself/demos/yourproject/yourproject.js
   3:22  error    Strings must use doublequote                  quotes
   6:14  warning  Missing function expression name              func-names
           :        :                :                            :
  20:26  error    Unexpected space before function parentheses  space-before-function-paren

✖ 13 problems (10 errors, 3 warnings)
```
##### Example Commands
```terminal
cd ~/${PARENT_DIR}/${PROJECT_NAME}/
eslint ${PROJECT_NAME}.js
```

.center[.footnote[.red.bold[] Code for this step.] ]
]

redpajama_set_name: RedPajamaGithub


text:
define(
   ({
    add: "Klicka för att lägga till ett nytt bokmärke.",
    title: "Titel",
    placeholderBookmarkName: "Namn på bokmärke",
    ok: "OK",
    cancel: "Avbryt",
    warning: "Slutför redigeringen!",
    edit: "Redigera bokmärke",
    errorNameExist: "Det finns ett bokmärke!",
    errorNameNull: "Ogiltigt namn på bokmärke!",
    addBookmark: "Skapa ett nytt bokmärke",
    thumbnail: "Miniatyrbild",
    thumbnailHint: "Klicka på bilden för att uppdatera"
  })
);


text:
typedef CGRect CGNSRect;
typedef UIView PlotGalleryNativeView;

#else

#import <CorePlot/CorePlot.h>
typedef NSRect CGNSRect;
typedef NSView PlotGalleryNativeView;
#endif

extern NSString *const kDemoPlots;
extern NSString *const kPieCharts;
extern NSString *const kLinePlots;
extern NSString *const kBarPlots;
extern NSString *const kFinancialPlots;

@class CPTGraph;
@class CPTTheme;

@interface PlotItem : NSObject

@property (nonatomic, readwrite, strong) CPTGraphHostingView *defaultLayerHostingView;

@property (nonatomic, readwrite, strong) NSMutableArray *graphs;
@property (nonatomic, readwrite, strong) NSString *section;
@property (nonatomic, readwrite, strong) NSString *title;

@property (nonatomic, readonly) CGFloat titleSize;

+(void)registerPlotItem:(id)item;

-(void)renderInView:(PlotGalleryNativeView *)hostingView withTheme:(CPTTheme *)theme animated:(BOOL)animated;
-(CPTNativeImage *)image;
#if TARGET_IPHONE_SIMULATOR || TARGET_OS_IPHONE
#else
-(void)setFrameSize:(NSSize)size;
#endif

-(void)renderInGraphHostingView:(CPTGraphHostingView *)hostingView withTheme:(CPTTheme *)theme animated:(BOOL)animated;

-(void)formatAllGraphs;

-(void)reloadData;
-(void)applyTheme:(CPTTheme *)theme toGraph:(CPTGraph *)graph withDefault:(CPTTheme *)defaultTheme;

-(void)addGraph:(CPTGraph *)graph;
-(void)addGraph:(CPTGraph *)graph toHostingView:(CPTGraphHostingView *)hostingView;
-(void)killGraph;

-(void)generateData;

-(NSComparisonResult)titleCompare:(PlotItem *)other;

@end


text:
package main

import (
	"io"
	"os"

	"github.com/flynn/flynn-controller/client"
	"github.com/flynn/go-flynn/demultiplex"
)

var cmdLog = &Command{
	Run:   runLog,
	Usage: "log [-s] <job>",
	Short: "get job log",
	Long:  `Stream log for a specific job`,
}

var logSplitOut bool

func init() {
	cmdLog.Flag.BoolVarP(&logSplitOut, "split-stderr", "s", false, "send stderr lines to stderr")
}

func runLog(cmd *Command, args []string, client *controller.Client) error {
	if len(args) != 1 {
		cmd.printUsage(true)
	}

	rc, err := client.GetJobLog(mustApp(), args[0])
	if err != nil {
		return err
	}
	var stderr io.Writer
	if logSplitOut {
		stderr = os.Stderr
	}
	demultiplex.Copy(os.Stdout, stderr, rc)
	rc.Close()
	return nil
}


text:
namespace Colore.Effects.Keypad
{
    using JetBrains.Annotations;

    /// <summary>
    /// Keypad constants.
    /// </summary>
    public static class KeypadConstants
    {
        /// <summary>
        /// Maximum number of rows.
        /// </summary>
        [PublicAPI]
        public const int MaxRows = 4;

        /// <summary>
        /// Maximum number of columns.
        /// </summary>
        [PublicAPI]
        public const int MaxColumns = 5;

        /// <summary>
        /// Total number of keys.
        /// </summary>
        [PublicAPI]
        public const int MaxKeys = MaxRows * MaxColumns;
    }
}


text:
/**
 *
 */
package org.sikuli.guide;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.util.ArrayList;

import org.sikuli.script.Debug;
import org.sikuli.script.Region;
import org.sikuli.script.Screen;

import com.sun.crypto.provider.RSACipher;

public class SikuliGuideArea extends SikuliGuideComponent
implements ComponentListener{

   ArrayList<Region> regions = new ArrayList<Region>();

   ArrayList<SikuliGuideComponent> landmarks = new ArrayList<SikuliGuideComponent>();

   public SikuliGuideArea(){
      super();
      // default to transparent so it can be faded in when it becomes visible later
      setOpacity(0);
   }

   public static final int BOUNDING = 0;
   public static final int INTERSECTION = 1;

   int relationship = BOUNDING;
   public void setRelationship(int relationship){
      this.relationship = relationship;
   }

   int mode = 0;
   public static int VERTICAL = 1;
   public static int HORIZONTAL = 2;
   public void setMode(int mode){
      this.mode = mode;
   }

   // update the bounds to the union of all the rectangles
   void updateBounds(){

      Rectangle rect = null;
      Screen s = new Screen();

      for (SikuliGuideComponent comp : landmarks){

         if (rect == null){
            rect = new Rectangle(comp.getBounds());
            continue;
         }else {

            if (relationship == BOUNDING){
               rect.add(comp.getBounds());
            }else if (relationship == INTERSECTION){
               rect = rect.intersection(comp.getBounds());
            }

         }
      }

      if (rect.width<0 || rect.height<=0){
         setVisible(false);
      }else{
         setVisible(true);

//         for (SikuliGuideComponent sklComp : getFollowers())
         // hack to get the locations of the followers to update

         if (mode == 0){
            setActualLocation(rect.x,rect.y);
            setActualSize(rect.getSize());
         } else if (mode == VERTICAL){
            setActualLocation(rect.x,0);
            setActualSize(rect.width, s.h);
         } else if (mode == HORIZONTAL){
            setActualLocation(0, rect.y);
            setActualSize(s.w, rect.height);
         }
      }

      updateVisibility();
   }

   public void addLandmark(SikuliGuideComponent comp){
      landmarks.add(comp);
      updateBounds();
      comp.addComponentListener(this);
   }

   public void addRegion(Region region){

      if (regions.isEmpty()){

         setActualBounds(region.getRect());

      }else{

         Rectangle bounds = getBounds();
         bounds.add(region.getRect());
         setActualBounds(bounds);

      }

      regions.add(region);
   }

   public void paintComponent(Graphics g){
      super.paintComponent(g);
      Graphics2D g2d = (Graphics2D)g;

      if (false){
      Rectangle r = getActualBounds();
      g2d.setColor(Color.black);
      g2d.drawRect(0,0,r.width-1,r.height-1);
      //g2d.setColor(Color.white);
      g2d.setColor(Color.green);
      g2d.setStroke(new BasicStroke(3f));
      g2d.drawRect(1,1,r.width-3,r.height-3);
      }
   }

   private void updateVisibility(){
      boolean allHidden = true;
      for (SikuliGuideComponent landmark : landmarks){
         allHidden = allHidden && !landmark.isVisible();
      }

      if (allHidden){
         //Debug.info("Area is hidden");
      }
      setVisible(!allHidden);

      // if area is visible, do fadein
      if (isVisible()){
         addFadeinAnimation();
         startAnimation();
      }
   }

   @Override
   public void componentHidden(ComponentEvent e) {
      updateVisibility();
   }

   @Override
   public void componentMoved(ComponentEvent e) {
      Rectangle r = getBounds();
      updateBounds();
      r.add(getBounds());
      if (getTopLevelAncestor() != null)
         getTopLevelAncestor().repaint(r.x,r.y,r.width,r.height);
   }

   @Override
   public void componentResized(ComponentEvent e) {
   }

   @Override
   public void componentShown(ComponentEvent e) {
      setVisible(true);
   }

}


text:
package io.grpc.netty;

import static io.netty.handler.codec.http2.DefaultHttp2LocalFlowController.DEFAULT_WINDOW_UPDATE_RATIO;
import static io.netty.util.CharsetUtil.UTF_8;
import static io.netty.util.internal.ObjectUtil.checkNotNull;

import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
import com.google.common.base.Stopwatch;
import com.google.common.base.Supplier;
import io.grpc.Attributes;
import io.grpc.ChannelLogger;
import io.grpc.InternalChannelz;
import io.grpc.Metadata;
import io.grpc.Status;
import io.grpc.StatusException;
import io.grpc.internal.ClientStreamListener.RpcProgress;
import io.grpc.internal.ClientTransport.PingCallback;
import io.grpc.internal.GrpcAttributes;
import io.grpc.internal.GrpcUtil;
import io.grpc.internal.Http2Ping;
import io.grpc.internal.InUseStateAggregator;
import io.grpc.internal.KeepAliveManager;
import io.grpc.internal.TransportTracer;
import io.grpc.netty.GrpcHttp2HeadersUtils.GrpcHttp2ClientHeadersDecoder;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufUtil;
import io.netty.buffer.Unpooled;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelPromise;
import io.netty.handler.codec.http2.DecoratingHttp2FrameWriter;
import io.netty.handler.codec.http2.DefaultHttp2Connection;
import io.netty.handler.codec.http2.DefaultHttp2ConnectionDecoder;
import io.netty.handler.codec.http2.DefaultHttp2ConnectionEncoder;
import io.netty.handler.codec.http2.DefaultHttp2FrameReader;
import io.netty.handler.codec.http2.DefaultHttp2FrameWriter;
import io.netty.handler.codec.http2.DefaultHttp2LocalFlowController;
import io.netty.handler.codec.http2.DefaultHttp2RemoteFlowController;
import io.netty.handler.codec.http2.Http2CodecUtil;
import io.netty.handler.codec.http2.Http2Connection;
import io.netty.handler.codec.http2.Http2ConnectionAdapter;
import io.netty.handler.codec.http2.Http2ConnectionDecoder;
import io.netty.handler.codec.http2.Http2ConnectionEncoder;
import io.netty.handler.codec.http2.Http2Error;
import io.netty.handler.codec.http2.Http2Exception;
import io.netty.handler.codec.http2.Http2FrameAdapter;
import io.netty.handler.codec.http2.Http2FrameLogger;
import io.netty.handler.codec.http2.Http2FrameReader;
import io.netty.handler.codec.http2.Http2FrameWriter;
import io.netty.handler.codec.http2.Http2Headers;
import io.netty.handler.codec.http2.Http2HeadersDecoder;
import io.netty.handler.codec.http2.Http2InboundFrameLogger;
import io.netty.handler.codec.http2.Http2OutboundFrameLogger;
import io.netty.handler.codec.http2.Http2Settings;
import io.netty.handler.codec.http2.Http2Stream;
import io.netty.handler.codec.http2.Http2StreamVisitor;
import io.netty.handler.codec.http2.StreamBufferingEncoder;
import io.netty.handler.codec.http2.WeightedFairQueueByteDistributor;
import io.netty.handler.logging.LogLevel;
import io.perfmark.PerfMark;
import io.perfmark.Tag;
import java.nio.channels.ClosedChannelException;
import java.util.concurrent.Executor;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.Nullable;

/**
 * Client-side Netty handler for GRPC processing. All event handlers are executed entirely within
 * the context of the Netty Channel thread.
 */
class NettyClientHandler extends AbstractNettyHandler {
  private static final Logger logger = Logger.getLogger(NettyClientHandler.class.getName());

  /**
   * A message that simply passes through the channel without any real processing. It is useful to
   * check if buffers have been drained and test the health of the channel in a single operation.
   */
  static final Object NOOP_MESSAGE = new Object();

  /**
   * Status used when the transport has exhausted the number of streams.
   */
  private static final Status EXHAUSTED_STREAMS_STATUS =
          Status.UNAVAILABLE.withDescription("Stream IDs have been exhausted");
  private static final long USER_PING_PAYLOAD = 1111;

  private final Http2Connection.PropertyKey streamKey;
  private final ClientTransportLifecycleManager lifecycleManager;
  private final KeepAliveManager keepAliveManager;
  // Returns new unstarted stopwatches
  private final Supplier<Stopwatch> stopwatchFactory;
  private final TransportTracer transportTracer;
  private final Attributes eagAttributes;
  private final String authority;
  private final InUseStateAggregator<Http2Stream> inUseState =
      new InUseStateAggregator<Http2Stream>() {
        @Override
        protected void handleInUse() {
          lifecycleManager.notifyInUse(true);
        }

        @Override
        protected void handleNotInUse() {
          lifecycleManager.notifyInUse(false);
        }
      };

  private WriteQueue clientWriteQueue;
  private Http2Ping ping;
  private Attributes attributes;
  private InternalChannelz.Security securityInfo;
  private Status abruptGoAwayStatus;
  private Status channelInactiveReason;

  static NettyClientHandler newHandler(
      ClientTransportLifecycleManager lifecycleManager,
      @Nullable KeepAliveManager keepAliveManager,
      boolean autoFlowControl,
      int flowControlWindow,
      int maxHeaderListSize,
      Supplier<Stopwatch> stopwatchFactory,
      Runnable tooManyPingsRunnable,
      TransportTracer transportTracer,
      Attributes eagAttributes,
      String authority,
      ChannelLogger negotiationLogger) {
    Preconditions.checkArgument(maxHeaderListSize > 0, "maxHeaderListSize must be positive");
    Http2HeadersDecoder headersDecoder = new GrpcHttp2ClientHeadersDecoder(maxHeaderListSize);
    Http2FrameReader frameReader = new DefaultHttp2FrameReader(headersDecoder);
    Http2FrameWriter frameWriter = new DefaultHttp2FrameWriter();
    Http2Connection connection = new DefaultHttp2Connection(false);
    WeightedFairQueueByteDistributor dist = new WeightedFairQueueByteDistributor(connection);
    dist.allocationQuantum(16 * 1024); // Make benchmarks fast again.
    DefaultHttp2RemoteFlowController controller =
        new DefaultHttp2RemoteFlowController(connection, dist);
    connection.remote().flowController(controller);

    return newHandler(
        connection,
        frameReader,
        frameWriter,
        lifecycleManager,
        keepAliveManager,
        autoFlowControl,
        flowControlWindow,
        maxHeaderListSize,
        stopwatchFactory,
        tooManyPingsRunnable,
        transportTracer,
        eagAttributes,
        authority,
        negotiationLogger);
  }

  @VisibleForTesting
  static NettyClientHandler newHandler(
      final Http2Connection connection,
      Http2FrameReader frameReader,
      Http2FrameWriter frameWriter,
      ClientTransportLifecycleManager lifecycleManager,
      KeepAliveManager keepAliveManager,
      boolean autoFlowControl,
      int flowControlWindow,
      int maxHeaderListSize,
      Supplier<Stopwatch> stopwatchFactory,
      Runnable tooManyPingsRunnable,
      TransportTracer transportTracer,
      Attributes eagAttributes,
      String authority,
      ChannelLogger negotiationLogger) {
    Preconditions.checkNotNull(connection, "connection");
    Preconditions.checkNotNull(frameReader, "frameReader");
    Preconditions.checkNotNull(lifecycleManager, "lifecycleManager");
    Preconditions.checkArgument(flowControlWindow > 0, "flowControlWindow must be positive");
    Preconditions.checkArgument(maxHeaderListSize > 0, "maxHeaderListSize must be positive");
    Preconditions.checkNotNull(stopwatchFactory, "stopwatchFactory");
    Preconditions.checkNotNull(tooManyPingsRunnable, "tooManyPingsRunnable");
    Preconditions.checkNotNull(eagAttributes, "eagAttributes");
    Preconditions.checkNotNull(authority, "authority");

    Http2FrameLogger frameLogger = new Http2FrameLogger(LogLevel.DEBUG, NettyClientHandler.class);
    frameReader = new Http2InboundFrameLogger(frameReader, frameLogger);
    frameWriter = new Http2OutboundFrameLogger(frameWriter, frameLogger);

    PingCountingFrameWriter pingCounter;
    frameWriter = pingCounter = new PingCountingFrameWriter(frameWriter);

    StreamBufferingEncoder encoder =
        new StreamBufferingEncoder(
            new DefaultHttp2ConnectionEncoder(connection, frameWriter));

    // Create the local flow controller configured to auto-refill the connection window.
    connection.local().flowController(
        new DefaultHttp2LocalFlowController(connection, DEFAULT_WINDOW_UPDATE_RATIO, true));

    Http2ConnectionDecoder decoder = new DefaultHttp2ConnectionDecoder(connection, encoder,
        frameReader);

    transportTracer.setFlowControlWindowReader(new Utils.FlowControlReader(connection));

    Http2Settings settings = new Http2Settings();
    settings.pushEnabled(false);
    settings.initialWindowSize(flowControlWindow);
    settings.maxConcurrentStreams(0);
    settings.maxHeaderListSize(maxHeaderListSize);

    return new NettyClientHandler(
        decoder,
        encoder,
        settings,
        negotiationLogger,
        lifecycleManager,
        keepAliveManager,
        stopwatchFactory,
        tooManyPingsRunnable,
        transportTracer,
        eagAttributes,
        authority,
        autoFlowControl,
        pingCounter);
  }

  private NettyClientHandler(
      Http2ConnectionDecoder decoder,
      Http2ConnectionEncoder encoder,
      Http2Settings settings,
      ChannelLogger negotiationLogger,
      ClientTransportLifecycleManager lifecycleManager,
      KeepAliveManager keepAliveManager,
      Supplier<Stopwatch> stopwatchFactory,
      final Runnable tooManyPingsRunnable,
      TransportTracer transportTracer,
      Attributes eagAttributes,
      String authority,
      boolean autoFlowControl,
      PingLimiter pingLimiter) {
    super(/* channelUnused= */ null, decoder, encoder, settings,
        negotiationLogger, autoFlowControl, pingLimiter);
    this.lifecycleManager = lifecycleManager;
    this.keepAliveManager = keepAliveManager;
    this.stopwatchFactory = stopwatchFactory;
    this.transportTracer = Preconditions.checkNotNull(transportTracer);
    this.eagAttributes = eagAttributes;
    this.authority = authority;
    this.attributes = Attributes.newBuilder()
        .set(GrpcAttributes.ATTR_CLIENT_EAG_ATTRS, eagAttributes).build();

    // Set the frame listener on the decoder.
    decoder().frameListener(new FrameListener());

    Http2Connection connection = encoder.connection();
    streamKey = connection.newKey();

    connection.addListener(new Http2ConnectionAdapter() {
      @Override
      public void onGoAwayReceived(int lastStreamId, long errorCode, ByteBuf debugData) {
        byte[] debugDataBytes = ByteBufUtil.getBytes(debugData);
        goingAway(errorCode, debugDataBytes);
        if (errorCode == Http2Error.ENHANCE_YOUR_CALM.code()) {
          String data = new String(debugDataBytes, UTF_8);
          logger.log(
              Level.WARNING, "Received GOAWAY with ENHANCE_YOUR_CALM. Debug data: {0}", data);
          if ("too_many_pings".equals(data)) {
            tooManyPingsRunnable.run();
          }
        }
      }

      @Override
      public void onStreamActive(Http2Stream stream) {
        if (connection().numActiveStreams() == 1
            && NettyClientHandler.this.keepAliveManager != null) {
          NettyClientHandler.this.keepAliveManager.onTransportActive();
        }
      }

      @Override
      public void onStreamClosed(Http2Stream stream) {
        // Although streams with CALL_OPTIONS_RPC_OWNED_BY_BALANCER are not marked as "in-use" in
        // the first place, we don't propagate that option here, and it's safe to reset the in-use
        // state for them, which will be a cheap no-op.
        inUseState.updateObjectInUse(stream, false);
        if (connection().numActiveStreams() == 0
            && NettyClientHandler.this.keepAliveManager != null) {
          NettyClientHandler.this.keepAliveManager.onTransportIdle();
        }
      }
    });
  }

  /**
   * The protocol negotiation attributes, available once the protocol negotiation completes;
   * otherwise returns {@code Attributes.EMPTY}.
   */
  Attributes getAttributes() {
    return attributes;
  }

  /**
   * Handler for commands sent from the stream.
   */
  @Override
  public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise)
          throws Exception {
    if (msg instanceof CreateStreamCommand) {
      createStream((CreateStreamCommand) msg, promise);
    } else if (msg instanceof SendGrpcFrameCommand) {
      sendGrpcFrame(ctx, (SendGrpcFrameCommand) msg, promise);
    } else if (msg instanceof CancelClientStreamCommand) {
      cancelStream(ctx, (CancelClientStreamCommand) msg, promise);
    } else if (msg instanceof SendPingCommand) {
      sendPingFrame(ctx, (SendPingCommand) msg, promise);
    } else if (msg instanceof GracefulCloseCommand) {
      gracefulClose(ctx, (GracefulCloseCommand) msg, promise);
    } else if (msg instanceof ForcefulCloseCommand) {
      forcefulClose(ctx, (ForcefulCloseCommand) msg, promise);
    } else if (msg == NOOP_MESSAGE) {
      ctx.write(Unpooled.EMPTY_BUFFER, promise);
    } else {
      throw new AssertionError("Write called for unexpected type: " + msg.getClass().getName());
    }
  }

  void startWriteQueue(Channel channel) {
    clientWriteQueue = new WriteQueue(channel);
  }

  WriteQueue getWriteQueue() {
    return clientWriteQueue;
  }

  ClientTransportLifecycleManager getLifecycleManager() {
    return lifecycleManager;
  }

  /**
   * Returns the given processed bytes back to inbound flow control.
   */
  void returnProcessedBytes(Http2Stream stream, int bytes) {
    try {
      decoder().flowController().consumeBytes(stream, bytes);
    } catch (Http2Exception e) {
      throw new RuntimeException(e);
    }
  }

  private void onHeadersRead(int streamId, Http2Headers headers, boolean endStream) {
    // Stream 1 is reserved for the Upgrade response, so we should ignore its headers here:
    if (streamId != Http2CodecUtil.HTTP_UPGRADE_STREAM_ID) {
      NettyClientStream.TransportState stream = clientStream(requireHttp2Stream(streamId));
      PerfMark.event("NettyClientHandler.onHeadersRead", stream.tag());
      stream.transportHeadersReceived(headers, endStream);
    }

    if (keepAliveManager != null) {
      keepAliveManager.onDataReceived();
    }
  }

  /**
   * Handler for an inbound HTTP/2 DATA frame.
   */
  private void onDataRead(int streamId, ByteBuf data, int padding, boolean endOfStream) {
    flowControlPing().onDataRead(data.readableBytes(), padding);
    NettyClientStream.TransportState stream = clientStream(requireHttp2Stream(streamId));
    PerfMark.event("NettyClientHandler.onDataRead", stream.tag());
    stream.transportDataReceived(data, endOfStream);
    if (keepAliveManager != null) {
      keepAliveManager.onDataReceived();
    }
  }

  /**
   * Handler for an inbound HTTP/2 RST_STREAM frame, terminating a stream.
   */
  private void onRstStreamRead(int streamId, long errorCode) {
    NettyClientStream.TransportState stream = clientStream(connection().stream(streamId));
    if (stream != null) {
      PerfMark.event("NettyClientHandler.onRstStreamRead", stream.tag());
      Status status = statusFromH2Error(null, "RST_STREAM closed stream", errorCode, null);
      stream.transportReportStatus(
          status,
          errorCode == Http2Error.REFUSED_STREAM.code()
              ? RpcProgress.REFUSED : RpcProgress.PROCESSED,
          false /*stop delivery*/,
          new Metadata());
      if (keepAliveManager != null) {
        keepAliveManager.onDataReceived();
      }
    }
  }

  @Override
  public void close(ChannelHandlerContext ctx, ChannelPromise promise) throws Exception {
    logger.fine("Network channel being closed by the application.");
    if (ctx.channel().isActive()) { // Ignore notification that the socket was closed
      lifecycleManager.notifyShutdown(
          Status.UNAVAILABLE.withDescription("Transport closed for unknown reason"));
    }
    super.close(ctx, promise);
  }

  /**
   * Handler for the Channel shutting down.
   */
  @Override
  public void channelInactive(ChannelHandlerContext ctx) throws Exception {
    try {
      logger.fine("Network channel is closed");
      Status status = Status.UNAVAILABLE.withDescription("Network closed for unknown reason");
      lifecycleManager.notifyShutdown(status);
      final Status streamStatus;
      if (channelInactiveReason != null) {
        streamStatus = channelInactiveReason;
      } else {
        streamStatus = lifecycleManager.getShutdownStatus();
      }
      try {
        cancelPing(lifecycleManager.getShutdownThrowable());
        // Report status to the application layer for any open streams
        connection().forEachActiveStream(new Http2StreamVisitor() {
          @Override
          public boolean visit(Http2Stream stream) throws Http2Exception {
            NettyClientStream.TransportState clientStream = clientStream(stream);
            if (clientStream != null) {
              clientStream.transportReportStatus(streamStatus, false, new Metadata());
            }
            return true;
          }
        });
      } finally {
        lifecycleManager.notifyTerminated(status);
      }
    } finally {
      // Close any open streams
      super.channelInactive(ctx);
      if (keepAliveManager != null) {
        keepAliveManager.onTransportTermination();
      }
    }
  }

  @Override
  public void handleProtocolNegotiationCompleted(
      Attributes attributes, InternalChannelz.Security securityInfo) {
    this.attributes = this.attributes.toBuilder().setAll(attributes).build();
    this.securityInfo = securityInfo;
    super.handleProtocolNegotiationCompleted(attributes, securityInfo);
    writeBufferingAndRemove(ctx().channel());
  }

  static void writeBufferingAndRemove(Channel channel) {
    checkNotNull(channel, "channel");
    ChannelHandlerContext handlerCtx =
        channel.pipeline().context(WriteBufferingAndExceptionHandler.class);
    if (handlerCtx == null) {
      return;
    }
    ((WriteBufferingAndExceptionHandler) handlerCtx.handler()).writeBufferedAndRemove(handlerCtx);
  }

  @Override
  public Attributes getEagAttributes() {
    return eagAttributes;
  }

  @Override
  public String getAuthority() {
    return authority;
  }

  InternalChannelz.Security getSecurityInfo() {
    return securityInfo;
  }

  @Override
  protected void onConnectionError(ChannelHandlerContext ctx,  boolean outbound, Throwable cause,
      Http2Exception http2Ex) {
    logger.log(Level.FINE, "Caught a connection error", cause);
    lifecycleManager.notifyShutdown(Utils.statusFromThrowable(cause));
    // Parent class will shut down the Channel
    super.onConnectionError(ctx, outbound, cause, http2Ex);
  }

  @Override
  protected void onStreamError(ChannelHandlerContext ctx, boolean outbound, Throwable cause,
      Http2Exception.StreamException http2Ex) {
    // Close the stream with a status that contains the cause.
    NettyClientStream.TransportState stream = clientStream(connection().stream(http2Ex.streamId()));
    if (stream != null) {
      stream.transportReportStatus(Utils.statusFromThrowable(cause), false, new Metadata());
    } else {
      logger.log(Level.FINE, "Stream error for unknown stream " + http2Ex.streamId(), cause);
    }

    // Delegate to the base class to send a RST_STREAM.
    super.onStreamError(ctx, outbound, cause, http2Ex);
  }

  @Override
  protected boolean isGracefulShutdownComplete() {
    // Only allow graceful shutdown to complete after all pending streams have completed.
    return super.isGracefulShutdownComplete()
        && ((StreamBufferingEncoder) encoder()).numBufferedStreams() == 0;
  }

  /**
   * Attempts to create a new stream from the given command. If there are too many active streams,
   * the creation request is queued.
   */
  private void createStream(CreateStreamCommand command, ChannelPromise promise)
          throws Exception {
    if (lifecycleManager.getShutdownThrowable() != null) {
      command.stream().setNonExistent();
      // The connection is going away (it is really the GOAWAY case),
      // just terminate the stream now.
      command.stream().transportReportStatus(
          lifecycleManager.getShutdownStatus(), RpcProgress.REFUSED, true, new Metadata());
      promise.setFailure(lifecycleManager.getShutdownThrowable());
      return;
    }

    // Get the stream ID for the new stream.
    int streamId;
    try {
      streamId = incrementAndGetNextStreamId();
    } catch (StatusException e) {
      command.stream().setNonExistent();
      // Stream IDs have been exhausted for this connection. Fail the promise immediately.
      promise.setFailure(e);

      // Initiate a graceful shutdown if we haven't already.
      if (!connection().goAwaySent()) {
        logger.fine("Stream IDs have been exhausted for this connection. "
                + "Initiating graceful shutdown of the connection.");
        lifecycleManager.notifyShutdown(e.getStatus());
        close(ctx(), ctx().newPromise());
      }
      return;
    }
    if (connection().goAwayReceived()) {
      Status s = abruptGoAwayStatus;
      int maxActiveStreams = connection().local().maxActiveStreams();
      int lastStreamId = connection().local().lastStreamKnownByPeer();
      if (s == null) {
        // Should be impossible, but handle pseudo-gracefully
        s = Status.INTERNAL.withDescription(
            "Failed due to abrupt GOAWAY, but can't find GOAWAY details");
      } else if (streamId > lastStreamId) {
        s = s.augmentDescription(
            "stream id: " + streamId + ", GOAWAY Last-Stream-ID:" + lastStreamId);
      } else if (connection().local().numActiveStreams() == maxActiveStreams) {
        s = s.augmentDescription("At MAX_CONCURRENT_STREAMS limit. limit: " + maxActiveStreams);
      }
      if (streamId > lastStreamId || connection().local().numActiveStreams() == maxActiveStreams) {
        // This should only be reachable during onGoAwayReceived, as otherwise
        // getShutdownThrowable() != null
        command.stream().setNonExistent();
        command.stream().transportReportStatus(s, RpcProgress.REFUSED, true, new Metadata());
        promise.setFailure(s.asRuntimeException());
        return;
      }
    }

    NettyClientStream.TransportState stream = command.stream();
    Http2Headers headers = command.headers();
    stream.setId(streamId);

    PerfMark.startTask("NettyClientHandler.createStream", stream.tag());
    PerfMark.linkIn(command.getLink());
    try {
      createStreamTraced(
          streamId, stream, headers, command.isGet(), command.shouldBeCountedForInUse(), promise);
    } finally {
      PerfMark.stopTask("NettyClientHandler.createStream", stream.tag());
    }
  }

  private void createStreamTraced(
      final int streamId,
      final NettyClientStream.TransportState stream,
      final Http2Headers headers,
      boolean isGet,
      final boolean shouldBeCountedForInUse,
      final ChannelPromise promise) {
    // Create an intermediate promise so that we can intercept the failure reported back to the
    // application.
    ChannelPromise tempPromise = ctx().newPromise();
    encoder().writeHeaders(ctx(), streamId, headers, 0, isGet, tempPromise)
        .addListener(new ChannelFutureListener() {
          @Override
          public void operationComplete(ChannelFuture future) throws Exception {
            if (future.isSuccess()) {
              // The http2Stream will be null in case a stream buffered in the encoder
              // was canceled via RST_STREAM.
              Http2Stream http2Stream = connection().stream(streamId);
              if (http2Stream != null) {
                stream.getStatsTraceContext().clientOutboundHeaders();
                http2Stream.setProperty(streamKey, stream);

                // This delays the in-use state until the I/O completes, which technically may
                // be later than we would like.
                if (shouldBeCountedForInUse) {
                  inUseState.updateObjectInUse(http2Stream, true);
                }

                // Attach the client stream to the HTTP/2 stream object as user data.
                stream.setHttp2Stream(http2Stream);
              }
              // Otherwise, the stream has been cancelled and Netty is sending a
              // RST_STREAM frame which causes it to purge pending writes from the
              // flow-controller and delete the http2Stream. The stream listener has already
              // been notified of cancellation so there is nothing to do.

              // Just forward on the success status to the original promise.
              promise.setSuccess();
            } else {
              final Throwable cause = future.cause();
              if (cause instanceof StreamBufferingEncoder.Http2GoAwayException) {
                StreamBufferingEncoder.Http2GoAwayException e =
                    (StreamBufferingEncoder.Http2GoAwayException) cause;
                Status status = statusFromH2Error(
                    Status.Code.UNAVAILABLE, "GOAWAY closed buffered stream",
                    e.errorCode(), e.debugData());
                stream.transportReportStatus(status, RpcProgress.REFUSED, true, new Metadata());
                promise.setFailure(status.asRuntimeException());
              } else {
                promise.setFailure(cause);
              }
            }
          }
        });
  }

  /**
   * Cancels this stream.
   */
  private void cancelStream(ChannelHandlerContext ctx, CancelClientStreamCommand cmd,
      ChannelPromise promise) {
    NettyClientStream.TransportState stream = cmd.stream();
    PerfMark.startTask("NettyClientHandler.cancelStream", stream.tag());
    PerfMark.linkIn(cmd.getLink());
    try {
      Status reason = cmd.reason();
      if (reason != null) {
        stream.transportReportStatus(reason, true, new Metadata());
      }
      if (!cmd.stream().isNonExistent()) {
        encoder().writeRstStream(ctx, stream.id(), Http2Error.CANCEL.code(), promise);
      } else {
        promise.setSuccess();
      }
    } finally {
      PerfMark.stopTask("NettyClientHandler.cancelStream", stream.tag());
    }
  }

  /**
   * Sends the given GRPC frame for the stream.
   */
  private void sendGrpcFrame(ChannelHandlerContext ctx, SendGrpcFrameCommand cmd,
      ChannelPromise promise) {
    PerfMark.startTask("NettyClientHandler.sendGrpcFrame", cmd.stream().tag());
    PerfMark.linkIn(cmd.getLink());
    try {
      // Call the base class to write the HTTP/2 DATA frame.
      // Note: no need to flush since this is handled by the outbound flow controller.
      encoder().writeData(ctx, cmd.stream().id(), cmd.content(), 0, cmd.endStream(), promise);
    } finally {
      PerfMark.stopTask("NettyClientHandler.sendGrpcFrame", cmd.stream().tag());
    }
  }

  private void sendPingFrame(ChannelHandlerContext ctx, SendPingCommand msg,
      ChannelPromise promise) {
    PerfMark.startTask("NettyClientHandler.sendPingFrame");
    PerfMark.linkIn(msg.getLink());
    try {
      sendPingFrameTraced(ctx, msg, promise);
    } finally {
      PerfMark.stopTask("NettyClientHandler.sendPingFrame");
    }
  }

  /**
   * Sends a PING frame. If a ping operation is already outstanding, the callback in the message is
   * registered to be called when the existing operation completes, and no new frame is sent.
   */
  private void sendPingFrameTraced(ChannelHandlerContext ctx, SendPingCommand msg,
      ChannelPromise promise) {
    // Don't check lifecycleManager.getShutdownStatus() since we want to allow pings after shutdown
    // but before termination. After termination, messages will no longer arrive because the
    // pipeline clears all handlers on channel close.

    PingCallback callback = msg.callback();
    Executor executor = msg.executor();
    // we only allow one outstanding ping at a time, so just add the callback to
    // any outstanding operation
    if (ping != null) {
      promise.setSuccess();
      ping.addCallback(callback, executor);
      return;
    }

    // Use a new promise to prevent calling the callback twice on write failure: here and in
    // NettyClientTransport.ping(). It may appear strange, but it will behave the same as if
    // ping != null above.
    promise.setSuccess();
    promise = ctx().newPromise();
    // set outstanding operation
    long data = USER_PING_PAYLOAD;
    Stopwatch stopwatch = stopwatchFactory.get();
    stopwatch.start();
    ping = new Http2Ping(data, stopwatch);
    ping.addCallback(callback, executor);
    // and then write the ping
    encoder().writePing(ctx, false, USER_PING_PAYLOAD, promise);
    ctx.flush();
    final Http2Ping finalPing = ping;
    promise.addListener(new ChannelFutureListener() {
      @Override
      public void operationComplete(ChannelFuture future) throws Exception {
        if (future.isSuccess()) {
          transportTracer.reportKeepAliveSent();
        } else {
          Throwable cause = future.cause();
          if (cause instanceof ClosedChannelException) {
            cause = lifecycleManager.getShutdownThrowable();
            if (cause == null) {
              cause = Status.UNKNOWN.withDescription("Ping failed but for unknown reason.")
                  .withCause(future.cause()).asException();
            }
          }
          finalPing.failed(cause);
          if (ping == finalPing) {
            ping = null;
          }
        }
      }
    });
  }

  private void gracefulClose(ChannelHandlerContext ctx, GracefulCloseCommand msg,
      ChannelPromise promise) throws Exception {
    lifecycleManager.notifyShutdown(msg.getStatus());
    // Explicitly flush to create any buffered streams before sending GOAWAY.
    // TODO(ejona): determine if the need to flush is a bug in Netty
    flush(ctx);
    close(ctx, promise);
  }

  private void forcefulClose(final ChannelHandlerContext ctx, final ForcefulCloseCommand msg,
      ChannelPromise promise) throws Exception {
    connection().forEachActiveStream(new Http2StreamVisitor() {
      @Override
      public boolean visit(Http2Stream stream) throws Http2Exception {
        NettyClientStream.TransportState clientStream = clientStream(stream);
        Tag tag = clientStream != null ? clientStream.tag() : PerfMark.createTag();
        PerfMark.startTask("NettyClientHandler.forcefulClose", tag);
        PerfMark.linkIn(msg.getLink());
        try {
          if (clientStream != null) {
            clientStream.transportReportStatus(msg.getStatus(), true, new Metadata());
            resetStream(ctx, stream.id(), Http2Error.CANCEL.code(), ctx.newPromise());
          }
          stream.close();
          return true;
        } finally {
          PerfMark.stopTask("NettyClientHandler.forcefulClose", tag);
        }
      }
    });
    close(ctx, promise);
  }

  /**
   * Handler for a GOAWAY being received. Fails any streams created after the
   * last known stream. May only be called during a read.
   */
  private void goingAway(long errorCode, byte[] debugData) {
    Status finalStatus = statusFromH2Error(
        Status.Code.UNAVAILABLE, "GOAWAY shut down transport", errorCode, debugData);
    lifecycleManager.notifyGracefulShutdown(finalStatus);
    abruptGoAwayStatus = statusFromH2Error(
        Status.Code.UNAVAILABLE, "Abrupt GOAWAY closed unsent stream", errorCode, debugData);
    // While this _should_ be UNAVAILABLE, Netty uses the wrong stream id in the GOAWAY when it
    // fails streams due to HPACK failures (e.g., header list too large). To be more conservative,
    // we assume any sent streams may be related to the GOAWAY. This should rarely impact users
    // since the main time servers should use abrupt GOAWAYs is if there is a protocol error, and if
    // there wasn't a protocol error the error code was probably NO_ERROR which is mapped to
    // UNAVAILABLE. https://github.com/netty/netty/issues/10670
    final Status abruptGoAwayStatusConservative = statusFromH2Error(
        null, "Abrupt GOAWAY closed sent stream", errorCode, debugData);
    final boolean mayBeHittingNettyBug = errorCode != Http2Error.NO_ERROR.code();
    // Try to allocate as many in-flight streams as possible, to reduce race window of
    // https://github.com/grpc/grpc-java/issues/2562 . To be of any help, the server has to
    // gracefully shut down the connection with two GOAWAYs. gRPC servers generally send a PING
    // after the first GOAWAY, so they can very precisely detect when the GOAWAY has been
    // processed and thus this processing must be in-line before processing additional reads.

    // This can cause reentrancy, but should be minor since it is normal to handle writes in
    // response to a read. Also, the call stack is rather shallow at this point
    clientWriteQueue.drainNow();
    if (lifecycleManager.notifyShutdown(finalStatus)) {
      // This is for the only RPCs that are actually covered by the GOAWAY error code. All other
      // RPCs were not observed by the remote and so should be UNAVAILABLE.
      channelInactiveReason = statusFromH2Error(
          null, "Connection closed after GOAWAY", errorCode, debugData);
    }

    final int lastKnownStream = connection().local().lastStreamKnownByPeer();
    try {
      connection().forEachActiveStream(new Http2StreamVisitor() {
        @Override
        public boolean visit(Http2Stream stream) throws Http2Exception {
          if (stream.id() > lastKnownStream) {
            NettyClientStream.TransportState clientStream = clientStream(stream);
            if (clientStream != null) {
              // RpcProgress _should_ be REFUSED, but are being conservative. See comment for
              // abruptGoAwayStatusConservative. This does reduce our ability to perform transparent
              // retries, but only if something else caused a connection failure.
              RpcProgress progress = mayBeHittingNettyBug
                  ? RpcProgress.PROCESSED
                  : RpcProgress.REFUSED;
              clientStream.transportReportStatus(
                  abruptGoAwayStatusConservative, progress, false, new Metadata());
            }
            stream.close();
          }
          return true;
        }
      });
    } catch (Http2Exception e) {
      throw new RuntimeException(e);
    }
  }

  private void cancelPing(Throwable t) {
    if (ping != null) {
      ping.failed(t);
      ping = null;
    }
  }

  /** If {@code statusCode} is non-null, it will be used instead of the http2 error code mapping. */
  private Status statusFromH2Error(
      Status.Code statusCode, String context, long errorCode, byte[] debugData) {
    Status status = GrpcUtil.Http2Error.statusForCode((int) errorCode);
    if (statusCode == null) {
      statusCode = status.getCode();
    }
    String debugString = "";
    if (debugData != null && debugData.length > 0) {
      // If a debug message was provided, use it.
      debugString = ", debug data: " + new String(debugData, UTF_8);
    }
    return statusCode.toStatus()
        .withDescription(context + ". " + status.getDescription() + debugString);
  }

  /**
   * Gets the client stream associated to the given HTTP/2 stream object.
   */
  private NettyClientStream.TransportState clientStream(Http2Stream stream) {
    return stream == null ? null : (NettyClientStream.TransportState) stream.getProperty(streamKey);
  }

  private int incrementAndGetNextStreamId() throws StatusException {
    int nextStreamId = connection().local().incrementAndGetNextStreamId();
    if (nextStreamId < 0) {
      logger.fine("Stream IDs have been exhausted for this connection. "
              + "Initiating graceful shutdown of the connection.");
      throw EXHAUSTED_STREAMS_STATUS.asException();
    }
    return nextStreamId;
  }

  private Http2Stream requireHttp2Stream(int streamId) {
    Http2Stream stream = connection().stream(streamId);
    if (stream == null) {
      // This should never happen.
      throw new AssertionError("Stream does not exist: " + streamId);
    }
    return stream;
  }

  private class FrameListener extends Http2FrameAdapter {
    private boolean firstSettings = true;

    @Override
    public void onSettingsRead(ChannelHandlerContext ctx, Http2Settings settings) {
      if (firstSettings) {
        firstSettings = false;
        lifecycleManager.notifyReady();
      }
    }

    @Override
    public int onDataRead(ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding,
        boolean endOfStream) throws Http2Exception {
      NettyClientHandler.this.onDataRead(streamId, data, padding, endOfStream);
      return padding;
    }

    @Override
    public void onHeadersRead(ChannelHandlerContext ctx,
        int streamId,
        Http2Headers headers,
        int streamDependency,
        short weight,
        boolean exclusive,
        int padding,
        boolean endStream) throws Http2Exception {
      NettyClientHandler.this.onHeadersRead(streamId, headers, endStream);
    }

    @Override
    public void onRstStreamRead(ChannelHandlerContext ctx, int streamId, long errorCode)
        throws Http2Exception {
      NettyClientHandler.this.onRstStreamRead(streamId, errorCode);
    }

    @Override
    public void onPingAckRead(ChannelHandlerContext ctx, long ackPayload) throws Http2Exception {
      Http2Ping p = ping;
      if (ackPayload == flowControlPing().payload()) {
        flowControlPing().updateWindow();
        if (logger.isLoggable(Level.FINE)) {
          logger.log(Level.FINE, String.format("Window: %d",
              decoder().flowController().initialWindowSize(connection().connectionStream())));
        }
      } else if (p != null) {
        if (p.payload() == ackPayload) {
          p.complete();
          ping = null;
        } else {
          logger.log(Level.WARNING, String.format(
              "Received unexpected ping ack. Expecting %d, got %d", p.payload(), ackPayload));
        }
      } else {
        logger.warning("Received unexpected ping ack. No ping outstanding");
      }
      if (keepAliveManager != null) {
        keepAliveManager.onDataReceived();
      }
    }

    @Override
    public void onPingRead(ChannelHandlerContext ctx, long data) throws Http2Exception {
      if (keepAliveManager != null) {
        keepAliveManager.onDataReceived();
      }
    }
  }

  private static class PingCountingFrameWriter extends DecoratingHttp2FrameWriter
      implements AbstractNettyHandler.PingLimiter {
    private int pingCount;

    public PingCountingFrameWriter(Http2FrameWriter delegate) {
      super(delegate);
    }

    @Override
    public boolean isPingAllowed() {
      // "3 strikes" may cause the server to complain, so we limit ourselves to 2 or below.
      return pingCount < 2;
    }

    @Override
    public ChannelFuture writeHeaders(
        ChannelHandlerContext ctx, int streamId, Http2Headers headers,
        int padding, boolean endStream, ChannelPromise promise) {
      pingCount = 0;
      return super.writeHeaders(ctx, streamId, headers, padding, endStream, promise);
    }

    @Override
    public ChannelFuture writeHeaders(
        ChannelHandlerContext ctx, int streamId, Http2Headers headers,
        int streamDependency, short weight, boolean exclusive,
        int padding, boolean endStream, ChannelPromise promise) {
      pingCount = 0;
      return super.writeHeaders(ctx, streamId, headers, streamDependency, weight, exclusive,
          padding, endStream, promise);
    }

    @Override
    public ChannelFuture writeWindowUpdate(
        ChannelHandlerContext ctx, int streamId, int windowSizeIncrement, ChannelPromise promise) {
      pingCount = 0;
      return super.writeWindowUpdate(ctx, streamId, windowSizeIncrement, promise);
    }

    @Override
    public ChannelFuture writePing(
        ChannelHandlerContext ctx, boolean ack, long data, ChannelPromise promise) {
      if (!ack) {
        pingCount++;
      }
      return super.writePing(ctx, ack, data, promise);
    }

    @Override
    public ChannelFuture writeData(
        ChannelHandlerContext ctx, int streamId, ByteBuf data, int padding, boolean endStream,
        ChannelPromise promise) {
      if (data.isReadable()) {
        pingCount = 0;
      }
      return super.writeData(ctx, streamId, data, padding, endStream, promise);
    }
  }
}


text:
package com.wixpress.petri.laboratory;

import com.google.common.base.Joiner;

import java.util.LinkedHashMap;
import java.util.Map;

public class ExperimentsLog {

    private final Map<String, String> experimentsResults;

    public ExperimentsLog(Map<String, String> experimentResults) {
        this.experimentsResults = experimentResults;
    }

    public static ExperimentsLog empty() {
        return ExperimentsLog.parse("");
    }

    public static ExperimentsLog parse(String runningExperiments) {
        try {
            Map<String, String> experiments = new LinkedHashMap<>();
            if (runningExperiments == null || runningExperiments.isEmpty())
                return new ExperimentsLog(experiments);

            for (String experiment : runningExperiments.split("\\|")) {
                String[] parsedExperiment = experiment.split("#");

                String key = parsedExperiment[0];
                String value = parsedExperiment[1];
                validateExperiment(key, value);

                experiments.put(key, value);
            }
            return new ExperimentsLog(experiments);
        } catch (Throwable t) {
            throw new MalformedExperimentsLogException(runningExperiments);
        }
    }

    private static void validateExperiment(String key, String value) {
        validateInteger(key);
        validateInteger(value);
    }

    private static void validateInteger(String i) {
        Integer.valueOf(i);
    }

    public String serialized() {
        return Joiner.on("|").withKeyValueSeparator("#").join(experimentsResults);
    }

    public boolean containsExperiment(int experimentId) {
        return experimentsResults.containsKey("" + experimentId);
    }

    public String winningGroupId(int experimentId) {
        return experimentsResults.get(String.valueOf(experimentId));
    }

    public ExperimentsLog appendExperiment(int experimentId, int testGroupId) {
        Map<String, String> updatedExperiments = new LinkedHashMap<String, String>(experimentsResults);
        updatedExperiments.put("" + experimentId, "" + testGroupId);
        return new ExperimentsLog(updatedExperiments);
    }

    public ExperimentsLog removeExperiment(int experimentId) {
        Map<String, String> updatedExperiments = new LinkedHashMap<String, String>(experimentsResults);
        updatedExperiments.remove("" + experimentId);
        return new ExperimentsLog(updatedExperiments);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        ExperimentsLog that = (ExperimentsLog) o;

        if (!experimentsResults.equals(that.experimentsResults)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = 0;
        result = 31 * result + experimentsResults.hashCode();
        return result;
    }

    @Override
    public String toString() {
        return "ExperimentsLog{" +
                ", experimentsResults=" + experimentsResults +
                '}';
    }

    public ExperimentsLog removeWhere(Predicate predicate) {
        LinkedHashMap<String, String> filteredResults = new LinkedHashMap<String, String>(experimentsResults);
        for (Map.Entry<String, String> s : experimentsResults.entrySet()) {
            if (predicate.matches(Integer.valueOf(s.getKey())))
                filteredResults.remove(s.getKey());
        }
        return new ExperimentsLog(filteredResults);
    }

    public int winningTestGroupId(int experimentId) {
        final String experimentValueAsString = winningGroupId(experimentId);
        return Integer.parseInt(experimentValueAsString);
    }

    public Map<String, String> getWinningTestGroups() {
        return experimentsResults;
    }

    public ExperimentsLog appendAll(ExperimentsLog other) {
        Map<String, String> combinedExperiments = new LinkedHashMap<>(experimentsResults);
        combinedExperiments.putAll(other.experimentsResults);
        return new ExperimentsLog(combinedExperiments);
    }

    public interface Predicate {
        boolean matches(int experimentId);
    }

}


text:
@implementation YGAllDetailCategoryItem
+ (NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass
{
    return @{
             @"articleList":@"YGAllDetailCategoryArticlelistItem"
             };
}

@end
@implementation YGAllDetailCategoryArticlelistItem

@end

@implementation YGAllDetailCategoryArticlelistObjectItem

+ (NSDictionary<NSString *,id> *)modelCustomPropertyMapper
{
    return @{
             @"ID":@"id",
             @"des":@"description"
             };
}

+ (NSDictionary<NSString *,id> *)modelContainerPropertyGenericClass
{
    return @{
            @"authorInfos":@"YGAllDetailCategoryArticlelistObjectAuthorinfoItem"
             };
}

@end

@implementation YGAllDetailCategoryArticlelistObjectAuthorinfoItem
+ (NSDictionary<NSString *,id> *)modelCustomPropertyMapper
{
    return @{
             @"ID":@"id"
             };
}
@end

@implementation YGAllDetailCategoryArticlelistObjectArticlesourceItem
+ (NSDictionary<NSString *,id> *)modelCustomPropertyMapper
{
    return @{
             @"ID":@"id"
             };
}
@end

@implementation YGAllDetailCategoryArticlelistObjectShareurlItem
+ (NSDictionary<NSString *,id> *)modelCustomPropertyMapper
{
    return @{
             @"des":@"description"
             };
}
@end

@implementation YGAllDetailCategoryArticlelistObjectAuthorinfosItem
+ (NSDictionary<NSString *,id> *)modelCustomPropertyMapper
{
    return @{
             @"ID":@"id"
             };
}
@end


text:
import Component from '../components/component.react';
import React from 'react';
import {FormattedHTMLMessage} from 'react-intl';
import {Link} from 'react-router';

export default class ToCheck extends Component {

  static propTypes = {
    msg: React.PropTypes.object.isRequired
  }

  render() {
    const {msg} = this.props;

    return (

{msg.header}

          {msg.itemListHtml.map(item =>

              <FormattedHTMLMessage message={item.txt} />

          )}

            {msg.isomorphicPage}{' '}
            <Link to="/this-is-not-the-web-page-you-are-looking-for">404</Link>.

            {msg.andMuchMore}

    );
  }

}


text:
* Now will not re-order values that contain any `var()` definitions.

# 2.2.0

* Adds support for re-ordering `transition` declarations.

# 2.1.1

* Fixes an issue where special comments were being discarded by this module.
  Now, property values with any comments in them will be ignored.

# 2.1.0

* Adds support for re-ordering `box-shadow` declarations.

# 2.0.2

* Bump postcss-value-parser to `3.0.1` (thanks to @TrySound).
* Fixes an issue where the module was discarding color codes if a `calc`
  function was found (thanks to @TrySound).

# 2.0.1

* Bump postcss-value-parser to `2.0.2`.

# 2.0.0

* Upgraded to PostCSS 5.

# 1.1.1

* Fixes an issue where `flex` properties were being mangled by the module.

# 1.1.0

* Adds support for `flex-flow` (thanks to @yisibl).

# 1.0.1

* The module will now recognise `auto` as a valid value.

# 1.0.0

* Initial release.


text:
A simple lazy purely functional language with parametric
polymorphism. The implementation contains a parser, type inference,
and an efficient interpreter. The language has integers, booleans,
lists, pairs, functions, and a general fixpoint operator.

Poly is very similar to miniHaskell. The only difference is that it
infers polymorphic types.

The file `example.minply` defines basic operations on lists.


text:
/* smallipop css */
.smallipop-hint {
  display: none;
}

#smallipop-tour-overlay {
  position: fixed;
  left: 0;
  top: 0;
  bottom: 0;
  right: 0;
}

.smallipop-instance {
  position: absolute;
  display: none;
  top: 0;
  left: 0;
  z-index: 9999;
  max-width: 400px;
}
.smallipop-instance font {
  size: 11px;
  family: arial;
}
.smallipop-instance a {
  color: #98cbea;
}
.smallipop-instance:before, .smallipop-instance:after {
  content: '';
  position: absolute;
  left: 50%;
  height: 0;
  width: 0;
  pointer-events: none;
}
.smallipop-instance:before {
  bottom: -16px;
  margin-left: -10px;
  border: 8px solid transparent;
}
.smallipop-instance:after {
  bottom: -20px;
  margin-left: -12px;
  border: 10px solid transparent;
}

.smallipop-align-left:before, .smallipop-align-left:after {
  margin-left: 0;
  left: auto;
  right: 20px;
}
.smallipop-align-left:after {
  right: 18px;
}

.smallipop-align-right:before, .smallipop-align-right:after {
  margin-left: 0;
  left: 20px;
  right: auto;
}
.smallipop-align-right:after {
  left: 18px;
}

.smallipop-bottom:before, .smallipop-bottom:after {
  bottom: auto;
  top: -20px;
}
.smallipop-bottom:after {
  top: -24px;
}

.smallipop-left:before, .smallipop-left:after,
.smallipop-right:before,
.smallipop-right:after {
  right: -16px;
  left: auto;
  top: 50%;
  bottom: auto;
  border-width: 8px;
  margin: -8px 0 0;
}
.smallipop-left:after,
.smallipop-right:after {
  right: -20px;
  border-width: 10px;
  margin: -10px 0 0;
}

.smallipop-right:before, .smallipop-right:after {
  left: -16px;
  right: auto;
}
.smallipop-right:after {
  left: -20px;
}

.smallipop-content {
  padding: 6px;
}

.smallipop-tour-content {
  padding: 5px 0;
  min-width: 150px;
}

.smallipop-tour-footer {
  padding-top: 5px;
  position: relative;
  overflow: hidden;
  *zoom: 1;
}

.smallipop-tour-progress {
  color: #bbb;
  text-align: center;
  position: absolute;
  left: 50%;
  width: 80px;
  margin-left: -40px;
  top: 8px;
}

.smallipop-tour-close-icon {
  position: absolute;
  right: -8px;
  top: -8px;
  width: 16px;
  height: 16px;
  padding-top: 0px;
  font-size: 11px;
  background: #555;
  color: #ccc;
  text-align: center;
  text-shadow: 0 -1px 1px #666666;
  text-decoration: none;
  -webkit-border-radius: 8px;
  -moz-border-radius: 8px;
  -ms-border-radius: 8px;
  -o-border-radius: 8px;
  border-radius: 8px;
  -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
}
.smallipop-tour-close-icon:hover {
  text-decoration: none;
  background: #666;
  color: #fff;
}

.smallipop-tour-prev,
.smallipop-tour-next,
.smallipop-tour-close {
  color: #ccc;
  display: block;
  padding: 3px 4px 2px;
  line-height: 1em;
  float: left;
  background: #203142;
  -webkit-border-radius: 3px;
  -moz-border-radius: 3px;
  -ms-border-radius: 3px;
  -o-border-radius: 3px;
  border-radius: 3px;
  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  -moz-box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
}
.smallipop-tour-prev:hover,
.smallipop-tour-next:hover,
.smallipop-tour-close:hover {
  color: #fff;
  background: #fff;
  text-decoration: none;
}

.smallipop-tour-next,
.smallipop-tour-close {
  float: right;
}

/* default theme */
.smallipop-theme-default {
  background: #FFF;
  color: #3B4052;
  font-family: 'Montserrat', 'Helvetica Neue', Helvetica, Arial, sans-serif;
  font-size: 0.75em;
  font-weight: 700;
}

.smallipop-theme-default:before {
  border-color: #FFF transparent transparent transparent;
}
.smallipop-theme-default:after {
  border-color: #FFF transparent transparent transparent;
}
.smallipop-theme-default.smallipop-bottom:before {
  border-color: transparent transparent #FFF transparent;
}
.smallipop-theme-default.smallipop-bottom:after {
  border-color: transparent transparent #FFF transparent;
}
.smallipop-theme-default.smallipop-left:before {
  border-color: transparent transparent transparent #FFF;
}
.smallipop-theme-default.smallipop-left:after {
  border-color: transparent transparent transparent #FFF;
}
.smallipop-theme-default.smallipop-right:before {
  border-color: transparent #FFF transparent transparent;
}
.smallipop-theme-default.smallipop-right:after {
  border-color: transparent #FFF transparent transparent;
}

.cssgradients.rgba .smallipop-theme-default {
  background-color: transparent;
}


text:
<?php
namespace bunq\Model\Generated\Endpoint;

use bunq\Context\ApiContext;
use bunq\Http\ApiClient;
use bunq\Http\BunqResponse;
use bunq\Model\Core\BunqModel;
use bunq\Model\Generated\Object\LabelMonetaryAccount;
use bunq\Model\Generated\Object\LabelUser;
use bunq\Model\Generated\Object\ShareDetail;

/**
 * Used to view or respond to shares a user was invited to. See
 * 'share-invite-bank-inquiry' for more information about the inquiring
 * endpoint.
 *
 * @generated
 */
class ShareInviteMonetaryAccountResponse extends BunqModel
{
    /**
     * Endpoint constants.
     */
    const ENDPOINT_URL_READ = 'user/%s/share-invite-monetary-account-response/%s';
    const ENDPOINT_URL_UPDATE = 'user/%s/share-invite-monetary-account-response/%s';
    const ENDPOINT_URL_LISTING = 'user/%s/share-invite-monetary-account-response';

    /**
     * Field constants.
     */
    const FIELD_STATUS = 'status';
    const FIELD_CARD_ID = 'card_id';

    /**
     * Object type.
     */
    const OBJECT_TYPE_GET = 'ShareInviteMonetaryAccountResponse';

    /**
     * The id of the ShareInviteBankResponse.
     *
     * @var int
     */
    protected $id;

    /**
     * The timestamp of the ShareInviteBankResponse creation.
     *
     * @var string
     */
    protected $created;

    /**
     * The timestamp of the ShareInviteBankResponse last update.
     *
     * @var string
     */
    protected $updated;

    /**
     * The monetary account and user who created the share.
     *
     * @var LabelMonetaryAccount
     */
    protected $counterAlias;

    /**
     * The user who cancelled the share if it has been revoked or rejected.
     *
     * @var LabelUser
     */
    protected $userAliasCancelled;

    /**
     * The id of the monetary account the ACCEPTED share applies to. null
     * otherwise.
     *
     * @var int
     */
    protected $monetaryAccountId;

    /**
     * The id of the draft share invite bank.
     *
     * @var int
     */
    protected $draftShareInviteBankId;

    /**
     * The share details.
     *
     * @var ShareDetail
     */
    protected $shareDetail;

    /**
     * The status of the share. Can be PENDING, REVOKED (the user deletes the
     * share inquiry before it's accepted), ACCEPTED, CANCELLED (the user
     * deletes an active share) or CANCELLATION_PENDING, CANCELLATION_ACCEPTED,
     * CANCELLATION_REJECTED (for canceling mutual connects)
     *
     * @var string
     */
    protected $status;

    /**
     * All of the relation users towards this MA.
     *
     * @var RelationUser
     */
    protected $relationUser;

    /**
     * The share type, either STANDARD or MUTUAL.
     *
     * @var string
     */
    protected $shareType;

    /**
     * The start date of this share.
     *
     * @var string
     */
    protected $startDate;

    /**
     * The expiration date of this share.
     *
     * @var string
     */
    protected $endDate;

    /**
     * The description of this share. It is basically the monetary account
     * description.
     *
     * @var string
     */
    protected $description;

    /**
     * The status of the share. Can be PENDING, REVOKED (the user deletes the
     * share inquiry before it's accepted), ACCEPTED, CANCELLED (the user
     * deletes an active share) or CANCELLATION_PENDING, CANCELLATION_ACCEPTED,
     * CANCELLATION_REJECTED (for canceling mutual connects)
     *
     * @var string|null
     */
    protected $statusFieldForRequest;

    /**
     * The card to link to the shared monetary account. Used only if
     * share_detail is ShareDetailCardPayment.
     *
     * @var int|null
     */
    protected $cardIdFieldForRequest;

    /**
     * @param string|null $status The status of the share. Can be PENDING,
     * REVOKED (the user deletes the share inquiry before it's accepted),
     * ACCEPTED, CANCELLED (the user deletes an active share) or
     * CANCELLATION_PENDING, CANCELLATION_ACCEPTED, CANCELLATION_REJECTED (for
     * canceling mutual connects)
     * @param int|null $cardId The card to link to the shared monetary account.
     * Used only if share_detail is ShareDetailCardPayment.
     */
    public function __construct(string  $status = null, int  $cardId = null)
    {
        $this->statusFieldForRequest = $status;
        $this->cardIdFieldForRequest = $cardId;
    }

    /**
     * Return the details of a specific share a user was invited to.
     *
     * @param int $shareInviteMonetaryAccountResponseId
     * @param string[] $customHeaders
     *
     * @return BunqResponseShareInviteMonetaryAccountResponse
     */
    public static function get(int $shareInviteMonetaryAccountResponseId, array $customHeaders = []): BunqResponseShareInviteMonetaryAccountResponse
    {
        $apiClient = new ApiClient(static::getApiContext());
        $responseRaw = $apiClient->get(
            vsprintf(
                self::ENDPOINT_URL_READ,
                [static::determineUserId(), $shareInviteMonetaryAccountResponseId]
            ),
            [],
            $customHeaders
        );

        return BunqResponseShareInviteMonetaryAccountResponse::castFromBunqResponse(
            static::fromJson($responseRaw, self::OBJECT_TYPE_GET)
        );
    }

    /**
     * Accept or reject a share a user was invited to.
     *
     * @param int $shareInviteMonetaryAccountResponseId
     * @param string|null $status The status of the share. Can be PENDING,
     * REVOKED (the user deletes the share inquiry before it's accepted),
     * ACCEPTED, CANCELLED (the user deletes an active share) or
     * CANCELLATION_PENDING, CANCELLATION_ACCEPTED, CANCELLATION_REJECTED (for
     * canceling mutual connects)
     * @param int|null $cardId The card to link to the shared monetary account.
     * Used only if share_detail is ShareDetailCardPayment.
     * @param string[] $customHeaders
     *
     * @return BunqResponseInt
     */
    public static function update(int $shareInviteMonetaryAccountResponseId, string  $status = null, int  $cardId = null, array $customHeaders = []): BunqResponseInt
    {
        $apiClient = new ApiClient(static::getApiContext());
        $responseRaw = $apiClient->put(
            vsprintf(
                self::ENDPOINT_URL_UPDATE,
                [static::determineUserId(), $shareInviteMonetaryAccountResponseId]
            ),
            [self::FIELD_STATUS => $status,
self::FIELD_CARD_ID => $cardId],
            $customHeaders
        );

        return BunqResponseInt::castFromBunqResponse(
            static::processForId($responseRaw)
        );
    }

    /**
     * Return all the shares a user was invited to.
     *
     * This method is called "listing" because "list" is a restricted PHP word
     * and cannot be used as constants, class names, function or method names.
     *
     * @param string[] $params
     * @param string[] $customHeaders
     *
     * @return BunqResponseShareInviteMonetaryAccountResponseList
     */
    public static function listing( array $params = [], array $customHeaders = []): BunqResponseShareInviteMonetaryAccountResponseList
    {
        $apiClient = new ApiClient(static::getApiContext());
        $responseRaw = $apiClient->get(
            vsprintf(
                self::ENDPOINT_URL_LISTING,
                [static::determineUserId()]
            ),
            $params,
            $customHeaders
        );

        return BunqResponseShareInviteMonetaryAccountResponseList::castFromBunqResponse(
            static::fromJsonList($responseRaw, self::OBJECT_TYPE_GET)
        );
    }

    /**
     * The id of the ShareInviteBankResponse.
     *
     * @return int
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param int $id
     */
    public function setId($id)
    {
        $this->id = $id;
    }

    /**
     * The timestamp of the ShareInviteBankResponse creation.
     *
     * @return string
     */
    public function getCreated()
    {
        return $this->created;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param string $created
     */
    public function setCreated($created)
    {
        $this->created = $created;
    }

    /**
     * The timestamp of the ShareInviteBankResponse last update.
     *
     * @return string
     */
    public function getUpdated()
    {
        return $this->updated;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param string $updated
     */
    public function setUpdated($updated)
    {
        $this->updated = $updated;
    }

    /**
     * The monetary account and user who created the share.
     *
     * @return LabelMonetaryAccount
     */
    public function getCounterAlias()
    {
        return $this->counterAlias;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param LabelMonetaryAccount $counterAlias
     */
    public function setCounterAlias($counterAlias)
    {
        $this->counterAlias = $counterAlias;
    }

    /**
     * The user who cancelled the share if it has been revoked or rejected.
     *
     * @return LabelUser
     */
    public function getUserAliasCancelled()
    {
        return $this->userAliasCancelled;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param LabelUser $userAliasCancelled
     */
    public function setUserAliasCancelled($userAliasCancelled)
    {
        $this->userAliasCancelled = $userAliasCancelled;
    }

    /**
     * The id of the monetary account the ACCEPTED share applies to. null
     * otherwise.
     *
     * @return int
     */
    public function getMonetaryAccountId()
    {
        return $this->monetaryAccountId;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param int $monetaryAccountId
     */
    public function setMonetaryAccountId($monetaryAccountId)
    {
        $this->monetaryAccountId = $monetaryAccountId;
    }

    /**
     * The id of the draft share invite bank.
     *
     * @return int
     */
    public function getDraftShareInviteBankId()
    {
        return $this->draftShareInviteBankId;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param int $draftShareInviteBankId
     */
    public function setDraftShareInviteBankId($draftShareInviteBankId)
    {
        $this->draftShareInviteBankId = $draftShareInviteBankId;
    }

    /**
     * The share details.
     *
     * @return ShareDetail
     */
    public function getShareDetail()
    {
        return $this->shareDetail;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param ShareDetail $shareDetail
     */
    public function setShareDetail($shareDetail)
    {
        $this->shareDetail = $shareDetail;
    }

    /**
     * The status of the share. Can be PENDING, REVOKED (the user deletes the
     * share inquiry before it's accepted), ACCEPTED, CANCELLED (the user
     * deletes an active share) or CANCELLATION_PENDING, CANCELLATION_ACCEPTED,
     * CANCELLATION_REJECTED (for canceling mutual connects)
     *
     * @return string
     */
    public function getStatus()
    {
        return $this->status;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param string $status
     */
    public function setStatus($status)
    {
        $this->status = $status;
    }

    /**
     * All of the relation users towards this MA.
     *
     * @return RelationUser
     */
    public function getRelationUser()
    {
        return $this->relationUser;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param RelationUser $relationUser
     */
    public function setRelationUser($relationUser)
    {
        $this->relationUser = $relationUser;
    }

    /**
     * The share type, either STANDARD or MUTUAL.
     *
     * @return string
     */
    public function getShareType()
    {
        return $this->shareType;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param string $shareType
     */
    public function setShareType($shareType)
    {
        $this->shareType = $shareType;
    }

    /**
     * The start date of this share.
     *
     * @return string
     */
    public function getStartDate()
    {
        return $this->startDate;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param string $startDate
     */
    public function setStartDate($startDate)
    {
        $this->startDate = $startDate;
    }

    /**
     * The expiration date of this share.
     *
     * @return string
     */
    public function getEndDate()
    {
        return $this->endDate;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param string $endDate
     */
    public function setEndDate($endDate)
    {
        $this->endDate = $endDate;
    }

    /**
     * The description of this share. It is basically the monetary account
     * description.
     *
     * @return string
     */
    public function getDescription()
    {
        return $this->description;
    }

    /**
     * @deprecated User should not be able to set values via setters, use
     * constructor.
     *
     * @param string $description
     */
    public function setDescription($description)
    {
        $this->description = $description;
    }

    /**
     * @return bool
     */
    public function isAllFieldNull()
    {
        if (!is_null($this->id)) {
            return false;
        }

        if (!is_null($this->created)) {
            return false;
        }

        if (!is_null($this->updated)) {
            return false;
        }

        if (!is_null($this->counterAlias)) {
            return false;
        }

        if (!is_null($this->userAliasCancelled)) {
            return false;
        }

        if (!is_null($this->monetaryAccountId)) {
            return false;
        }

        if (!is_null($this->draftShareInviteBankId)) {
            return false;
        }

        if (!is_null($this->shareDetail)) {
            return false;
        }

        if (!is_null($this->status)) {
            return false;
        }

        if (!is_null($this->relationUser)) {
            return false;
        }

        if (!is_null($this->shareType)) {
            return false;
        }

        if (!is_null($this->startDate)) {
            return false;
        }

        if (!is_null($this->endDate)) {
            return false;
        }

        if (!is_null($this->description)) {
            return false;
        }

        return true;
    }
}