public class

ConstraintSet

extends java.lang.Object

 java.lang.Object

↳androidx.constraintlayout.widget.ConstraintSet

Gradle dependencies

compile group: 'androidx.constraintlayout', name: 'constraintlayout', version: '2.2.0-beta01'

  • groupId: androidx.constraintlayout
  • artifactId: constraintlayout
  • version: 2.2.0-beta01

Artifact androidx.constraintlayout:constraintlayout:2.2.0-beta01 it located at Google repository (https://maven.google.com/)

Androidx artifact mapping:

androidx.constraintlayout:constraintlayout com.android.support.constraint:constraint-layout

Androidx class mapping:

androidx.constraintlayout.widget.ConstraintSet android.support.constraint.ConstraintSet

Overview

Defines a set of constraints to be used with ConstraintLayout.

ConstraintSet enables you create and save constraints and apply them to an existing ConstraintLayout. For details about constraint behaviour, see ConstraintLayout.

ConstraintsSet can be created in various ways:

  • Manually — c = new ConstraintSet(); c.connect(...);
  • From an R.layout.* object — c.clone(context, R.layout.layout1);
  • From a ConstraintLayout — c.clone(constraintLayout);

Example code:

import android.content.Context;
      import android.os.Bundle;
      import android.support.constraint.ConstraintLayout;
      import android.support.constraint.ConstraintSet;
      import android.support.transition.TransitionManager;
      import android.support.v7.app.AppCompatActivity;
      import android.view.View;

      public class MainActivity extends AppCompatActivity {
          ConstraintSet mConstraintSet1 = new ConstraintSet(); // Create a ConstraintSet.
          ConstraintSet mConstraintSet2 = new ConstraintSet(); // Create a ConstraintSet.
          ConstraintLayout mConstraintLayout; // Cache the ConstraintLayout.
          boolean mOld = true;


          protected void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              Context context = this;
              mConstraintSet2.clone(context, R.layout.state2); // Get constraints from layout.
              setContentView(R.layout.state1);
              mConstraintLayout = (ConstraintLayout) findViewById(R.id.activity_main);
              mConstraintSet1.clone(mConstraintLayout); // Get constraints from ConstraintSet.
          }

          public void foo(View view) {
              TransitionManager.beginDelayedTransition(mConstraintLayout);
              if (mOld = !mOld) {
                  mConstraintSet1.applyTo(mConstraintLayout); // Set new constraints.
              }  else {
                  mConstraintSet2.applyTo(mConstraintLayout); // Set new constraints.
              }
          }
      }

Summary

Fields
public static final intBASELINE

The baseline of the text in a view.

public static final intBOTTOM

The bottom side of a view.

public static final intCHAIN_PACKED

Chain packed style

public static final intCHAIN_SPREAD

Chain spread style

public static final intCHAIN_SPREAD_INSIDE

Chain spread inside style

public static final intCIRCLE_REFERENCE

Circle reference from a view.

public java.lang.StringderivedState

public static final intEND

The right side of a view in left to right languages.

public static final intGONE

This view is gone, and will not take any space for layout purposes.

public static final intHORIZONTAL

The horizontal orientation.

public static final intHORIZONTAL_GUIDELINE

Used to create a horizontal create guidelines.

public static final intINVISIBLE

This view is invisible, but it still takes up space for layout purposes.

public static final intLEFT

The left side of a view.

public static final intMATCH_CONSTRAINT

Dimension will be controlled by constraints

public static final intMATCH_CONSTRAINT_PERCENT

public static final intMATCH_CONSTRAINT_SPREAD

Calculate the size of a view in 0 dp by reducing the constrains gaps as much as possible

public static final intMATCH_CONSTRAINT_WRAP

How to calculate the size of a view in 0 dp by using its wrap_content size

public java.lang.StringmIdString

public intmRotate

public static final intPARENT_ID

References the id of the parent.

public static final intRIGHT

The right side of a view.

public static final intROTATE_LEFT_OF_PORTRATE

public static final intROTATE_NONE

public static final intROTATE_PORTRATE_OF_LEFT

public static final intROTATE_PORTRATE_OF_RIGHT

public static final intROTATE_RIGHT_OF_PORTRATE

public static final intSTART

The left side of a view in left to right languages.

public static final intTOP

The top of a view.

public static final intUNSET

Used to indicate a parameter is cleared or not set

public static final intVERTICAL

The vertical orientation.

public static final intVERTICAL_GUIDELINE

Used to create a vertical create guidelines.

public static final intVISIBILITY_MODE_IGNORE

public static final intVISIBILITY_MODE_NORMAL

public static final intVISIBLE

This view is visible.

public static final intWRAP_CONTENT

Dimension will set by the view's content

Constructors
publicConstraintSet()

Methods
public voidaddColorAttributes(java.lang.String attributeName[])

Add attribute of type Color

public voidaddFloatAttributes(java.lang.String attributeName[])

Add attribute of type float

public voidaddIntAttributes(java.lang.String attributeName[])

Add attribute of type Int

public voidaddStringAttributes(java.lang.String attributeName[])

Add attribute of type string

public voidaddToHorizontalChain(int viewId, int leftId, int rightId)

Adds a view to a horizontal chain.

public voidaddToHorizontalChainRTL(int viewId, int leftId, int rightId)

Adds a view to a horizontal chain.

public voidaddToVerticalChain(int viewId, int topId, int bottomId)

Adds a view to a vertical chain.

public voidapplyCustomAttributes(ConstraintLayout constraintLayout)

Apply custom attributes alone

public voidapplyDeltaFrom(ConstraintSet cs)

Get the delta form the ConstraintSet and aplly to this

public voidapplyTo(ConstraintLayout constraintLayout)

Apply the constraints to a ConstraintLayout.

public voidapplyToHelper(ConstraintHelper helper, ConstraintWidget child, ConstraintLayout.LayoutParams layoutParams, <any> mapIdToWidget)

Apply Layout to Helper widget

public voidapplyToLayoutParams(int id, ConstraintLayout.LayoutParams layoutParams)

Fill in a ConstraintLayout LayoutParam based on the id.

public voidapplyToWithoutCustom(ConstraintLayout constraintLayout)

Apply the constraints to a ConstraintLayout.

public static ConstraintSet.ConstraintbuildDelta(Context context, XmlPullParser parser)

Used to read a ConstraintDelta

public voidcenter(int centerID, int firstID, int firstSide, int firstMargin, int secondId, int secondSide, int secondMargin, float bias)

Center widget between the other two widgets.

public voidcenterHorizontally(int viewId, int toView)

Centers the view horizontally relative to toView's position.

public voidcenterHorizontally(int centerID, int leftId, int leftSide, int leftMargin, int rightId, int rightSide, int rightMargin, float bias)

Centers the widget horizontally to the left and right side on another widgets sides.

public voidcenterHorizontallyRtl(int viewId, int toView)

Centers the view horizontally relative to toView's position.

public voidcenterHorizontallyRtl(int centerID, int startId, int startSide, int startMargin, int endId, int endSide, int endMargin, float bias)

Centers the widgets horizontally to the left and right side on another widgets sides.

public voidcenterVertically(int viewId, int toView)

Centers the view vertically relative to toView's position.

public voidcenterVertically(int centerID, int topId, int topSide, int topMargin, int bottomId, int bottomSide, int bottomMargin, float bias)

Centers the widgets vertically to the top and bottom side on another widgets sides.

public voidclear(int viewId)

Remove all constraints from this view.

public voidclear(int viewId, int anchor)

Remove a constraint from this view.

public voidclone(ConstraintLayout constraintLayout)

Copy the layout parameters of a ConstraintLayout.

public voidclone(Constraints constraints)

Copy the layout parameters of a ConstraintLayout.

public voidclone(ConstraintSet set)

Copy the constraints from a layout.

public voidclone(Context context, int constraintLayoutId)

Copy the constraints from a layout.

public voidconnect(int startID, int startSide, int endID, int endSide)

Create a constraint between two widgets.

public voidconnect(int startID, int startSide, int endID, int endSide, int margin)

Create a constraint between two widgets.

public voidconstrainCircle(int viewId, int id, int radius, float angle)

Constrain the view on a circle constraint

public voidconstrainDefaultHeight(int viewId, int height)

Sets how the height is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.

public voidconstrainDefaultWidth(int viewId, int width)

Sets how the width is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.

public voidconstrainedHeight(int viewId, boolean constrained)

Sets how the height is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.

public voidconstrainedWidth(int viewId, boolean constrained)

Sets how the width is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.

public voidconstrainHeight(int viewId, int height)

Sets the height of the view.

public voidconstrainMaxHeight(int viewId, int height)

Sets the maximum height of the view.

public voidconstrainMaxWidth(int viewId, int width)

Sets the maximum width of the view.

public voidconstrainMinHeight(int viewId, int height)

Sets the height of the view.

public voidconstrainMinWidth(int viewId, int width)

Sets the width of the view.

public voidconstrainPercentHeight(int viewId, float percent)

Sets the height of the view as a percentage of the parent.

public voidconstrainPercentWidth(int viewId, float percent)

Sets the width of the view as a percentage of the parent.

public voidconstrainWidth(int viewId, int width)

Sets the width of the view.

public voidcreate(int guidelineID, int orientation)

Creates a ConstraintLayout virtual object.

public voidcreateBarrier(int id, int direction, int margin, int[] referenced[])

Creates a ConstraintLayout Barrier object.

public voidcreateHorizontalChain(int leftId, int leftSide, int rightId, int rightSide, int[] chainIds[], float[] weights[], int style)

Spaces a set of widgets horizontally between the view startID and endId.

public voidcreateHorizontalChainRtl(int startId, int startSide, int endId, int endSide, int[] chainIds[], float[] weights[], int style)

Spaces a set of widgets horizontal between the view startID and endId.

public voidcreateVerticalChain(int topId, int topSide, int bottomId, int bottomSide, int[] chainIds[], float[] weights[], int style)

Spaces a set of widgets vertically between the view topId and bottomId.

public voiddump(MotionScene scene, int[] ids[])

Dump the contents

public booleangetApplyElevation(int viewId)

return with the constraint set will apply elevation for the specified view.

public ConstraintSet.ConstraintgetConstraint(int id)

public java.util.HashMap<java.lang.String, ConstraintAttribute>getCustomAttributeSet()

public intgetHeight(int viewId)

Get the height set in the view

public int[]getKnownIds()

public ConstraintSet.ConstraintgetParameters(int mId)

public int[]getReferencedIds(int id)

get the reference id's of a helper.

public java.lang.StringgetStateLabels()

Get the types associated with this ConstraintSet The types mechanism allows you to tag the constraint set with a series of string to define properties of a ConstraintSet

public intgetVisibility(int viewId)

Get the visibility flag set in this view

public intgetVisibilityMode(int viewId)

ConstraintSet will not setVisibility.

public intgetWidth(int viewId)

Get the width set in the view

public booleanisForceId()

Enforce id are required for all ConstraintLayout children to use ConstraintSet.

public booleanisValidateOnParse()

If true perform validation checks when parsing ConstraintSets This will slow down parsing and should only be used for debugging

public voidload(Context context, int resourceId)

Load a constraint set from a constraintSet.xml file.

public voidload(Context context, XmlPullParser parser)

Load a constraint set from a constraintSet.xml file

public booleanmatchesLabels(java.lang.String types[])

Test if the list of strings matches labels defined on this constraintSet

public voidparseColorAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse color

public voidparseFloatAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse floats

public voidparseIntAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse int

public voidparseStringAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse string

public voidreadFallback(ConstraintLayout constraintLayout)

This will copy Constraints from the ConstraintLayout if it does not have parameters

public voidreadFallback(ConstraintSet set)

This will copy Constraints from the ConstraintSet

public voidremoveAttribute(java.lang.String attributeName)

Remove the attribute

public voidremoveFromHorizontalChain(int viewId)

Removes a view from a horizontal chain.

public voidremoveFromVerticalChain(int viewId)

Removes a view from a vertical chain.

public voidsetAlpha(int viewId, float alpha)

Adjust the alpha of a view.

public voidsetApplyElevation(int viewId, boolean apply)

set if elevation will be applied to the view.

public voidsetBarrierType(int id, int type)

SEt tye type of barier

public voidsetColorValue(int viewId, java.lang.String attributeName, int value)

Set the value of an attribute of type color

public voidsetDimensionRatio(int viewId, java.lang.String ratio)

Constrains the views aspect ratio.

public voidsetEditorAbsoluteX(int viewId, int position)

public voidsetEditorAbsoluteY(int viewId, int position)

public voidsetElevation(int viewId, float elevation)

Adjust the elevation of a view.

public voidsetFloatValue(int viewId, java.lang.String attributeName, float value)

Set the value of an attribute of type float

public voidsetForceId(boolean forceId)

Enforce id are required for all ConstraintLayout children to use ConstraintSet.

public voidsetGoneMargin(int viewId, int anchor, int value)

Sets the gone margin.

public voidsetGuidelineBegin(int guidelineID, int margin)

Set the guideline's distance form the top or left edge.

public voidsetGuidelineEnd(int guidelineID, int margin)

Set a guideline's distance to end.

public voidsetGuidelinePercent(int guidelineID, float ratio)

Set a Guideline's percent.

public voidsetHorizontalBias(int viewId, float bias)

Adjust the horizontal bias of the view (used with views constrained on left and right).

public voidsetHorizontalChainStyle(int viewId, int chainStyle)

How the elements of the horizontal chain will be positioned.

public voidsetHorizontalWeight(int viewId, float weight)

The child's weight that we can use to distribute the available horizontal space in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT

public voidsetIntValue(int viewId, java.lang.String attributeName, int value)

Set the value of an attribute of type int

public voidsetLayoutWrapBehavior(int viewId, int behavior)

Sets the wrap behavior of the widget in the parent's wrap computation

public voidsetMargin(int viewId, int anchor, int value)

Sets the margin.

public voidsetReferencedIds(int id, int[] referenced[])

sets the reference id's of a barrier.

public voidsetRotation(int viewId, float rotation)

Adjust the post-layout rotation about the Z axis of a view.

public voidsetRotationX(int viewId, float rotationX)

Adjust the post-layout rotation about the X axis of a view.

public voidsetRotationY(int viewId, float rotationY)

Adjust the post-layout rotation about the Y axis of a view.

public voidsetScaleX(int viewId, float scaleX)

Adjust the post-layout scale in X of a view.

public voidsetScaleY(int viewId, float scaleY)

Adjust the post-layout scale in Y of a view.

public voidsetStateLabels(java.lang.String types)

Set the types associated with this ConstraintSet The types mechanism allows you to tag the constraint set with a series of string to define properties of a ConstraintSet

public voidsetStateLabelsList(java.lang.String types[])

Set the types associated with this ConstraintSet The types mechanism allows you to tag the constraint set with a series of string to define properties of a ConstraintSet

public voidsetStringValue(int viewId, java.lang.String attributeName, java.lang.String value)

Set the value of an attribute of type string

public voidsetTransformPivot(int viewId, float transformPivotX, float transformPivotY)

Set X,Y location of the pivot point around which the view will rotate and scale.

public voidsetTransformPivotX(int viewId, float transformPivotX)

Set X location of the pivot point around which the view will rotate and scale.

public voidsetTransformPivotY(int viewId, float transformPivotY)

Set Y location of the pivot point around which the view will rotate and scale.

public voidsetTranslation(int viewId, float translationX, float translationY)

Adjust the post-layout translation of a view.

public voidsetTranslationX(int viewId, float translationX)

Adjust the post-layout X translation of a view.

public voidsetTranslationY(int viewId, float translationY)

Adjust the post-layout Y translation of a view.

public voidsetTranslationZ(int viewId, float translationZ)

Adjust the translation in Z of a view.

public voidsetValidateOnParse(boolean validate)

If true perform validation checks when parsing ConstraintSets This will slow down parsing and should only be used for debugging

public voidsetVerticalBias(int viewId, float bias)

Adjust the vertical bias of the view (used with views constrained on left and right).

public voidsetVerticalChainStyle(int viewId, int chainStyle)

How the elements of the vertical chain will be positioned.

public voidsetVerticalWeight(int viewId, float weight)

The child's weight that we can use to distribute the available vertical space in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT

public voidsetVisibility(int viewId, int visibility)

Adjust the visibility of a view.

public voidsetVisibilityMode(int viewId, int visibilityMode)

ConstraintSet will not setVisibility.

public voidwriteState(java.io.Writer writer, ConstraintLayout layout, int flags)

Write the state to a Writer

from java.lang.Objectclone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

Fields

public java.lang.String mIdString

public java.lang.String derivedState

public static final int ROTATE_NONE

public static final int ROTATE_PORTRATE_OF_RIGHT

public static final int ROTATE_PORTRATE_OF_LEFT

public static final int ROTATE_RIGHT_OF_PORTRATE

public static final int ROTATE_LEFT_OF_PORTRATE

public int mRotate

public static final int UNSET

Used to indicate a parameter is cleared or not set

public static final int MATCH_CONSTRAINT

Dimension will be controlled by constraints

public static final int WRAP_CONTENT

Dimension will set by the view's content

public static final int MATCH_CONSTRAINT_WRAP

How to calculate the size of a view in 0 dp by using its wrap_content size

public static final int MATCH_CONSTRAINT_SPREAD

Calculate the size of a view in 0 dp by reducing the constrains gaps as much as possible

public static final int MATCH_CONSTRAINT_PERCENT

public static final int PARENT_ID

References the id of the parent. Used in:

public static final int HORIZONTAL

The horizontal orientation.

public static final int VERTICAL

The vertical orientation.

public static final int HORIZONTAL_GUIDELINE

Used to create a horizontal create guidelines.

public static final int VERTICAL_GUIDELINE

Used to create a vertical create guidelines. see ConstraintSet.create(int, int)

public static final int VISIBLE

This view is visible. Use with ConstraintSet.setVisibility(int, int) and android:visibility.

public static final int INVISIBLE

This view is invisible, but it still takes up space for layout purposes. Use with ConstraintSet.setVisibility(int, int) and android:visibility.

public static final int GONE

This view is gone, and will not take any space for layout purposes. Use with ConstraintSet.setVisibility(int, int) and android:visibility.

public static final int LEFT

The left side of a view.

public static final int RIGHT

The right side of a view.

public static final int TOP

The top of a view.

public static final int BOTTOM

The bottom side of a view.

public static final int BASELINE

The baseline of the text in a view.

public static final int START

The left side of a view in left to right languages. In right to left languages it corresponds to the right side of the view

public static final int END

The right side of a view in left to right languages. In right to left languages it corresponds to the left side of the view

public static final int CIRCLE_REFERENCE

Circle reference from a view.

public static final int CHAIN_SPREAD

Chain spread style

public static final int CHAIN_SPREAD_INSIDE

Chain spread inside style

public static final int VISIBILITY_MODE_NORMAL

public static final int VISIBILITY_MODE_IGNORE

public static final int CHAIN_PACKED

Chain packed style

Constructors

public ConstraintSet()

Methods

public java.util.HashMap<java.lang.String, ConstraintAttribute> getCustomAttributeSet()

public ConstraintSet.Constraint getParameters(int mId)

Parameters:

mId:

Returns:

public void readFallback(ConstraintSet set)

This will copy Constraints from the ConstraintSet

Parameters:

set:

public void readFallback(ConstraintLayout constraintLayout)

This will copy Constraints from the ConstraintLayout if it does not have parameters

Parameters:

constraintLayout:

public void applyDeltaFrom(ConstraintSet cs)

Get the delta form the ConstraintSet and aplly to this

Parameters:

cs:

public java.lang.String getStateLabels()

Get the types associated with this ConstraintSet The types mechanism allows you to tag the constraint set with a series of string to define properties of a ConstraintSet

Returns:

an array of type strings

public void setStateLabels(java.lang.String types)

Set the types associated with this ConstraintSet The types mechanism allows you to tag the constraint set with a series of string to define properties of a ConstraintSet

Parameters:

types: a comer separated array of strings.

public void setStateLabelsList(java.lang.String types[])

Set the types associated with this ConstraintSet The types mechanism allows you to tag the constraint set with a series of string to define properties of a ConstraintSet

Parameters:

types: a comer separated array of strings.

public boolean matchesLabels(java.lang.String types[])

Test if the list of strings matches labels defined on this constraintSet

Parameters:

types: list of types

Returns:

true if all types are in the labels

public void clone(Context context, int constraintLayoutId)

Copy the constraints from a layout.

Parameters:

context: the context for the layout inflation
constraintLayoutId: the id of the layout file

public void clone(ConstraintSet set)

Copy the constraints from a layout.

Parameters:

set: constraint set to copy

public void clone(ConstraintLayout constraintLayout)

Copy the layout parameters of a ConstraintLayout.

Parameters:

constraintLayout: The ConstraintLayout to be copied

public void clone(Constraints constraints)

Copy the layout parameters of a ConstraintLayout.

Parameters:

constraints: The ConstraintLayout to be copied

public void applyTo(ConstraintLayout constraintLayout)

Apply the constraints to a ConstraintLayout.

Parameters:

constraintLayout: to be modified

public void applyToWithoutCustom(ConstraintLayout constraintLayout)

Apply the constraints to a ConstraintLayout.

Parameters:

constraintLayout: to be modified

public void applyCustomAttributes(ConstraintLayout constraintLayout)

Apply custom attributes alone

Parameters:

constraintLayout:

public void applyToHelper(ConstraintHelper helper, ConstraintWidget child, ConstraintLayout.LayoutParams layoutParams, <any> mapIdToWidget)

Apply Layout to Helper widget

Parameters:

helper:
child:
layoutParams:
mapIdToWidget:

public void applyToLayoutParams(int id, ConstraintLayout.LayoutParams layoutParams)

Fill in a ConstraintLayout LayoutParam based on the id.

Parameters:

id: Id of the view
layoutParams: LayoutParams to be filled

public void center(int centerID, int firstID, int firstSide, int firstMargin, int secondId, int secondSide, int secondMargin, float bias)

Center widget between the other two widgets. (for sides see: ConstraintSet.TOP, ConstraintSet.BOTTOM, ConstraintSet.START, ConstraintSet.END, ConstraintSet.LEFT, ConstraintSet.RIGHT) Note, sides must be all vertical or horizontal sides.

Parameters:

centerID: ID of the widget to be centered
firstID: ID of the first widget to connect the left or top of the widget to
firstSide: the side of the widget to connect to
firstMargin: the connection margin
secondId: the ID of the second widget to connect to right or top of the widget to
secondSide: the side of the widget to connect to
secondMargin: the connection margin
bias: the ratio between two connections

public void centerHorizontally(int centerID, int leftId, int leftSide, int leftMargin, int rightId, int rightSide, int rightMargin, float bias)

Centers the widget horizontally to the left and right side on another widgets sides. (for sides see: ConstraintSet.START, ConstraintSet.END, ConstraintSet.LEFT, ConstraintSet.RIGHT)

Parameters:

centerID: ID of widget to be centered
leftId: The Id of the widget on the left side
leftSide: The side of the leftId widget to connect to
leftMargin: The margin on the left side
rightId: The Id of the widget on the right side
rightSide: The side of the rightId widget to connect to
rightMargin: The margin on the right side
bias: The ratio of the space on the left vs. right sides 0.5 is centered (default)

public void centerHorizontallyRtl(int centerID, int startId, int startSide, int startMargin, int endId, int endSide, int endMargin, float bias)

Centers the widgets horizontally to the left and right side on another widgets sides. (for sides see: ConstraintSet.START, ConstraintSet.END, ConstraintSet.LEFT, ConstraintSet.RIGHT)

Parameters:

centerID: ID of widget to be centered
startId: The Id of the widget on the start side (left in non rtl languages)
startSide: The side of the startId widget to connect to
startMargin: The margin on the start side
endId: The Id of the widget on the start side (left in non rtl languages)
endSide: The side of the endId widget to connect to
endMargin: The margin on the end side
bias: The ratio of the space on the start vs end side 0.5 is centered (default)

public void centerVertically(int centerID, int topId, int topSide, int topMargin, int bottomId, int bottomSide, int bottomMargin, float bias)

Centers the widgets vertically to the top and bottom side on another widgets sides. (for sides see: ConstraintSet.TOP, ConstraintSet.BOTTOM)

Parameters:

centerID: ID of widget to be centered
topId: The Id of the widget on the top side
topSide: The side of the leftId widget to connect to
topMargin: The margin on the top side
bottomId: The Id of the widget on the bottom side
bottomSide: The side of the bottomId widget to connect to
bottomMargin: The margin on the bottom side
bias: The ratio of the space on the top vs. bottom sides 0.5 is centered (default)

public void createVerticalChain(int topId, int topSide, int bottomId, int bottomSide, int[] chainIds[], float[] weights[], int style)

Spaces a set of widgets vertically between the view topId and bottomId. Widgets can be spaced with weights. This operation sets all the related margins to 0.

(for sides see: ConstraintSet.TOP, ConstraintSet.BOTTOM)

Parameters:

topId: The id of the widget to connect to or PARENT_ID
topSide: the side of the start to connect to
bottomId: The id of the widget to connect to or PARENT_ID
bottomSide: the side of the right to connect to
chainIds: widgets to use as a chain
weights: can be null
style: set the style of the chain

public void createHorizontalChain(int leftId, int leftSide, int rightId, int rightSide, int[] chainIds[], float[] weights[], int style)

Spaces a set of widgets horizontally between the view startID and endId. Widgets can be spaced with weights. This operation sets all the related margins to 0.

(for sides see: ConstraintSet.START, ConstraintSet.END, ConstraintSet.LEFT, ConstraintSet.RIGHT)

Parameters:

leftId: The id of the widget to connect to or PARENT_ID
leftSide: the side of the start to connect to
rightId: The id of the widget to connect to or PARENT_ID
rightSide: the side of the right to connect to
chainIds: The widgets in the chain
weights: The weight to assign to each element in the chain or null
style: The type of chain

public void createHorizontalChainRtl(int startId, int startSide, int endId, int endSide, int[] chainIds[], float[] weights[], int style)

Spaces a set of widgets horizontal between the view startID and endId. Widgets can be spaced with weights. (for sides see: ConstraintSet.START, ConstraintSet.END, ConstraintSet.LEFT, ConstraintSet.RIGHT)

Parameters:

startId: The id of the widget to connect to or PARENT_ID
startSide: the side of the start to connect to
endId: The id of the widget to connect to or PARENT_ID
endSide: the side of the end to connect to
chainIds: The widgets in the chain
weights: The weight to assign to each element in the chain or null
style: The type of chain

public void connect(int startID, int startSide, int endID, int endSide, int margin)

Create a constraint between two widgets. (for sides see: ConstraintSet.TOP, ConstraintSet.BOTTOM, ConstraintSet.START, ConstraintSet.END, ConstraintSet.LEFT, ConstraintSet.RIGHT, ConstraintSet.BASELINE)

Parameters:

startID: the ID of the widget to be constrained
startSide: the side of the widget to constrain
endID: the id of the widget to constrain to
endSide: the side of widget to constrain to
margin: the margin to constrain (margin must be positive)

public void connect(int startID, int startSide, int endID, int endSide)

Create a constraint between two widgets. (for sides see: ConstraintSet.TOP, ConstraintSet.BOTTOM, ConstraintSet.START, ConstraintSet.END, ConstraintSet.LEFT, ConstraintSet.RIGHT, ConstraintSet.BASELINE)

Parameters:

startID: the ID of the widget to be constrained
startSide: the side of the widget to constrain
endID: the id of the widget to constrain to
endSide: the side of widget to constrain to

public void centerHorizontally(int viewId, int toView)

Centers the view horizontally relative to toView's position.

Parameters:

viewId: ID of view to center Horizontally
toView: ID of view to center on (or in)

public void centerHorizontallyRtl(int viewId, int toView)

Centers the view horizontally relative to toView's position.

Parameters:

viewId: ID of view to center Horizontally
toView: ID of view to center on (or in)

public void centerVertically(int viewId, int toView)

Centers the view vertically relative to toView's position.

Parameters:

viewId: ID of view to center Horizontally
toView: ID of view to center on (or in)

public void clear(int viewId)

Remove all constraints from this view.

Parameters:

viewId: ID of view to remove all connections to

public void clear(int viewId, int anchor)

Remove a constraint from this view.

Parameters:

viewId: ID of view to center on (or in)
anchor: the Anchor to remove constraint from

public void setMargin(int viewId, int anchor, int value)

Sets the margin.

Parameters:

viewId: ID of view to adjust the margin on
anchor: The side to adjust the margin on
value: The new value for the margin

public void setGoneMargin(int viewId, int anchor, int value)

Sets the gone margin.

Parameters:

viewId: ID of view to adjust the margin on
anchor: The side to adjust the margin on
value: The new value for the margin

public void setHorizontalBias(int viewId, float bias)

Adjust the horizontal bias of the view (used with views constrained on left and right).

Parameters:

viewId: ID of view to adjust the horizontal
bias: the new bias 0.5 is in the middle

public void setVerticalBias(int viewId, float bias)

Adjust the vertical bias of the view (used with views constrained on left and right).

Parameters:

viewId: ID of view to adjust the vertical
bias: the new bias 0.5 is in the middle

public void setDimensionRatio(int viewId, java.lang.String ratio)

Constrains the views aspect ratio. For Example a HD screen is 16 by 9 = 16/(float)9 = 1.777f.

Parameters:

viewId: ID of view to constrain
ratio: The ratio of the width to height (width / height)

public void setVisibility(int viewId, int visibility)

Adjust the visibility of a view.

Parameters:

viewId: ID of view to adjust the vertical
visibility: the visibility

public void setVisibilityMode(int viewId, int visibilityMode)

ConstraintSet will not setVisibility. ConstraintSet.VISIBILITY_MODE_IGNORE or ConstraintSet.VISIBILITY_MODE_NORMAL.

Parameters:

viewId: ID of view
visibilityMode:

public int getVisibilityMode(int viewId)

ConstraintSet will not setVisibility. ConstraintSet.VISIBILITY_MODE_IGNORE or ConstraintSet.VISIBILITY_MODE_NORMAL.

Parameters:

viewId: ID of view

public int getVisibility(int viewId)

Get the visibility flag set in this view

Parameters:

viewId: the id of the view

Returns:

the visibility constraint for the view

public int getHeight(int viewId)

Get the height set in the view

Parameters:

viewId: the id of the view

Returns:

return the height constraint of the view

public int getWidth(int viewId)

Get the width set in the view

Parameters:

viewId: the id of the view

Returns:

return the width constraint of the view

public void setAlpha(int viewId, float alpha)

Adjust the alpha of a view.

Parameters:

viewId: ID of view to adjust the vertical
alpha: the alpha

public boolean getApplyElevation(int viewId)

return with the constraint set will apply elevation for the specified view.

Returns:

true if the elevation will be set on this view (default is false)

public void setApplyElevation(int viewId, boolean apply)

set if elevation will be applied to the view. Elevation logic is based on style and animation. By default it is not used because it would lead to unexpected results.

Parameters:

apply: true if this constraint set applies elevation to this view

public void setElevation(int viewId, float elevation)

Adjust the elevation of a view.

Parameters:

viewId: ID of view to adjust the elevation
elevation: the elevation

public void setRotation(int viewId, float rotation)

Adjust the post-layout rotation about the Z axis of a view.

Parameters:

viewId: ID of view to adjust th Z rotation
rotation: the rotation about the X axis

public void setRotationX(int viewId, float rotationX)

Adjust the post-layout rotation about the X axis of a view.

Parameters:

viewId: ID of view to adjust th X rotation
rotationX: the rotation about the X axis

public void setRotationY(int viewId, float rotationY)

Adjust the post-layout rotation about the Y axis of a view.

Parameters:

viewId: ID of view to adjust the Y rotation
rotationY: the rotationY

public void setScaleX(int viewId, float scaleX)

Adjust the post-layout scale in X of a view.

Parameters:

viewId: ID of view to adjust the scale in X
scaleX: the scale in X

public void setScaleY(int viewId, float scaleY)

Adjust the post-layout scale in Y of a view.

Parameters:

viewId: ID of view to adjust the scale in Y
scaleY: the scale in Y

public void setTransformPivotX(int viewId, float transformPivotX)

Set X location of the pivot point around which the view will rotate and scale. use Float.NaN to clear the pivot value. Note: once an actual View has had its pivot set it cannot be cleared.

Parameters:

viewId: ID of view to adjust the transforms pivot point about X
transformPivotX: X location of the pivot point.

public void setTransformPivotY(int viewId, float transformPivotY)

Set Y location of the pivot point around which the view will rotate and scale. use Float.NaN to clear the pivot value. Note: once an actual View has had its pivot set it cannot be cleared.

Parameters:

viewId: ID of view to adjust the transforms pivot point about Y
transformPivotY: Y location of the pivot point.

public void setTransformPivot(int viewId, float transformPivotX, float transformPivotY)

Set X,Y location of the pivot point around which the view will rotate and scale. use Float.NaN to clear the pivot value. Note: once an actual View has had its pivot set it cannot be cleared.

Parameters:

viewId: ID of view to adjust the transforms pivot point
transformPivotX: X location of the pivot point.
transformPivotY: Y location of the pivot point.

public void setTranslationX(int viewId, float translationX)

Adjust the post-layout X translation of a view.

Parameters:

viewId: ID of view to translate in X
translationX: the translation in X

public void setTranslationY(int viewId, float translationY)

Adjust the post-layout Y translation of a view.

Parameters:

viewId: ID of view to to translate in Y
translationY: the translation in Y

public void setTranslation(int viewId, float translationX, float translationY)

Adjust the post-layout translation of a view.

Parameters:

viewId: ID of view to adjust its translation in X & Y
translationX: the translation in X
translationY: the translation in Y

public void setTranslationZ(int viewId, float translationZ)

Adjust the translation in Z of a view.

Parameters:

viewId: ID of view to adjust
translationZ: the translationZ

public void setEditorAbsoluteX(int viewId, int position)

public void setEditorAbsoluteY(int viewId, int position)

public void setLayoutWrapBehavior(int viewId, int behavior)

Sets the wrap behavior of the widget in the parent's wrap computation

public void constrainHeight(int viewId, int height)

Sets the height of the view. It can be a dimension, ConstraintSet.WRAP_CONTENT or ConstraintSet.MATCH_CONSTRAINT.

Parameters:

viewId: ID of view to adjust its height
height: the height of the view

public void constrainWidth(int viewId, int width)

Sets the width of the view. It can be a dimension, ConstraintSet.WRAP_CONTENT or ConstraintSet.MATCH_CONSTRAINT.

Parameters:

viewId: ID of view to adjust its width
width: the width of the view

public void constrainCircle(int viewId, int id, int radius, float angle)

Constrain the view on a circle constraint

Parameters:

viewId: ID of the view we constrain
id: ID of the view we constrain relative to
radius: the radius of the circle in degrees
angle: the angle

public void constrainMaxHeight(int viewId, int height)

Sets the maximum height of the view. It is a dimension, It is only applicable if height is #MATCH_CONSTRAINT}.

Parameters:

viewId: ID of view to adjust it height
height: the maximum height of the constraint

public void constrainMaxWidth(int viewId, int width)

Sets the maximum width of the view. It is a dimension, It is only applicable if width is #MATCH_CONSTRAINT}.

Parameters:

viewId: ID of view to adjust its max height
width: the maximum width of the view

public void constrainMinHeight(int viewId, int height)

Sets the height of the view. It is a dimension, It is only applicable if height is #MATCH_CONSTRAINT}.

Parameters:

viewId: ID of view to adjust its min height
height: the minimum height of the view

public void constrainMinWidth(int viewId, int width)

Sets the width of the view. It is a dimension, It is only applicable if width is #MATCH_CONSTRAINT}.

Parameters:

viewId: ID of view to adjust its min height
width: the minimum width of the view

public void constrainPercentWidth(int viewId, float percent)

Sets the width of the view as a percentage of the parent.

Parameters:

viewId:
percent:

public void constrainPercentHeight(int viewId, float percent)

Sets the height of the view as a percentage of the parent.

Parameters:

viewId:
percent:

public void constrainDefaultHeight(int viewId, int height)

Sets how the height is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD. Default is spread.

Parameters:

viewId: ID of view to adjust its matchConstraintDefaultHeight
height: MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD

public void constrainedWidth(int viewId, boolean constrained)

Sets how the width is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD. Default is spread.

Parameters:

viewId: ID of view to adjust its matchConstraintDefaultWidth
constrained: if true with will be constrained

public void constrainedHeight(int viewId, boolean constrained)

Sets how the height is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD. Default is spread.

Parameters:

viewId: ID of view to adjust its matchConstraintDefaultHeight
constrained: if true height will be constrained

public void constrainDefaultWidth(int viewId, int width)

Sets how the width is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD. Default is spread.

Parameters:

viewId: ID of view to adjust its matchConstraintDefaultWidth
width: SPREAD or WRAP

public void setHorizontalWeight(int viewId, float weight)

The child's weight that we can use to distribute the available horizontal space in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT

Parameters:

viewId: ID of view to adjust its HorizontalWeight
weight: the weight that we can use to distribute the horizontal space

public void setVerticalWeight(int viewId, float weight)

The child's weight that we can use to distribute the available vertical space in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT

Parameters:

viewId: ID of view to adjust its VerticalWeight
weight: the weight that we can use to distribute the vertical space

public void setHorizontalChainStyle(int viewId, int chainStyle)

How the elements of the horizontal chain will be positioned. if the dimension behaviour is set to MATCH_CONSTRAINT. The possible values are:

Parameters:

viewId: ID of view to adjust its HorizontalChainStyle
chainStyle: the weight that we can use to distribute the horizontal space

public void setVerticalChainStyle(int viewId, int chainStyle)

How the elements of the vertical chain will be positioned. in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT

Parameters:

viewId: ID of view to adjust its VerticalChainStyle
chainStyle: the weight that we can use to distribute the horizontal space

public void addToHorizontalChain(int viewId, int leftId, int rightId)

Adds a view to a horizontal chain.

Parameters:

viewId: view to add
leftId: view in chain to the left
rightId: view in chain to the right

public void addToHorizontalChainRTL(int viewId, int leftId, int rightId)

Adds a view to a horizontal chain.

Parameters:

viewId: view to add
leftId: view to the start side
rightId: view to the end side

public void addToVerticalChain(int viewId, int topId, int bottomId)

Adds a view to a vertical chain.

Parameters:

viewId: view to add to a vertical chain
topId: view above.
bottomId: view below

public void removeFromVerticalChain(int viewId)

Removes a view from a vertical chain. This assumes the view is connected to a vertical chain. Its behaviour is undefined if not part of a vertical chain.

Parameters:

viewId: the view to be removed

public void removeFromHorizontalChain(int viewId)

Removes a view from a horizontal chain. This assumes the view is connected to a horizontal chain. Its behaviour is undefined if not part of a horizontal chain.

Parameters:

viewId: the view to be removed

public void create(int guidelineID, int orientation)

Creates a ConstraintLayout virtual object. Currently only horizontal or vertical GuideLines.

Parameters:

guidelineID: ID of guideline to create
orientation: the Orientation of the guideline

public void createBarrier(int id, int direction, int margin, int[] referenced[])

Creates a ConstraintLayout Barrier object.

Parameters:

id:
direction: Barrier.{LEFT,RIGHT,TOP,BOTTOM,START,END}
referenced:

public void setGuidelineBegin(int guidelineID, int margin)

Set the guideline's distance form the top or left edge.

Parameters:

guidelineID: ID of the guideline
margin: the distance to the top or left edge

public void setGuidelineEnd(int guidelineID, int margin)

Set a guideline's distance to end.

Parameters:

guidelineID: ID of the guideline
margin: the margin to the right or bottom side of container

public void setGuidelinePercent(int guidelineID, float ratio)

Set a Guideline's percent.

Parameters:

guidelineID: ID of the guideline
ratio: the ratio between the gap on the left and right 0.0 is top/left 0.5 is middle

public int[] getReferencedIds(int id)

get the reference id's of a helper.

Parameters:

id:

Returns:

array of id's

public void setReferencedIds(int id, int[] referenced[])

sets the reference id's of a barrier.

Parameters:

id:
referenced:

public void setBarrierType(int id, int type)

SEt tye type of barier

Parameters:

id:
type:

public void removeAttribute(java.lang.String attributeName)

Remove the attribute

Parameters:

attributeName:

public void setIntValue(int viewId, java.lang.String attributeName, int value)

Set the value of an attribute of type int

Parameters:

viewId:
attributeName:
value:

public void setColorValue(int viewId, java.lang.String attributeName, int value)

Set the value of an attribute of type color

Parameters:

viewId:
attributeName:
value:

public void setFloatValue(int viewId, java.lang.String attributeName, float value)

Set the value of an attribute of type float

Parameters:

viewId:
attributeName:
value:

public void setStringValue(int viewId, java.lang.String attributeName, java.lang.String value)

Set the value of an attribute of type string

Parameters:

viewId:
attributeName:
value:

public void parseIntAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse int

Parameters:

set:
attributes:

public void parseColorAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse color

Parameters:

set:
attributes:

public void parseFloatAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse floats

Parameters:

set:
attributes:

public void parseStringAttributes(ConstraintSet.Constraint set, java.lang.String attributes)

Parse string

Parameters:

set:
attributes:

public void addIntAttributes(java.lang.String attributeName[])

Add attribute of type Int

Parameters:

attributeName:

public void addColorAttributes(java.lang.String attributeName[])

Add attribute of type Color

Parameters:

attributeName:

public void addFloatAttributes(java.lang.String attributeName[])

Add attribute of type float

Parameters:

attributeName:

public void addStringAttributes(java.lang.String attributeName[])

Add attribute of type string

Parameters:

attributeName:

public void load(Context context, int resourceId)

Load a constraint set from a constraintSet.xml file. Note. Do NOT use this to load a layout file. It will fail silently as there is no efficient way to differentiate.

Parameters:

context: the context for the inflation
resourceId: id of xml file in res/xml/

public void load(Context context, XmlPullParser parser)

Load a constraint set from a constraintSet.xml file

Parameters:

context: the context for the inflation
parser: id of xml file in res/xml/

public static ConstraintSet.Constraint buildDelta(Context context, XmlPullParser parser)

Used to read a ConstraintDelta

Parameters:

context:
parser:

Returns:

public ConstraintSet.Constraint getConstraint(int id)

public int[] getKnownIds()

public boolean isForceId()

Enforce id are required for all ConstraintLayout children to use ConstraintSet. default = true;

public void setForceId(boolean forceId)

Enforce id are required for all ConstraintLayout children to use ConstraintSet. default = true;

Parameters:

forceId:

public void setValidateOnParse(boolean validate)

If true perform validation checks when parsing ConstraintSets This will slow down parsing and should only be used for debugging

Parameters:

validate:

public boolean isValidateOnParse()

If true perform validation checks when parsing ConstraintSets This will slow down parsing and should only be used for debugging

Returns:

validate

public void dump(MotionScene scene, int[] ids[])

Dump the contents

Parameters:

scene:
ids:

public void writeState(java.io.Writer writer, ConstraintLayout layout, int flags)

Write the state to a Writer

Parameters:

writer:
layout:
flags:

Source

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package androidx.constraintlayout.widget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.Color;
import android.os.Build;
import android.os.Build.VERSION_CODES;
import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.util.Xml;
import android.view.LayoutInflater;
import android.view.View;

import androidx.constraintlayout.core.motion.utils.Easing;
import androidx.constraintlayout.core.widgets.ConstraintWidget;
import androidx.constraintlayout.core.widgets.HelperWidget;
import androidx.constraintlayout.motion.widget.Debug;
import androidx.constraintlayout.motion.widget.MotionLayout;
import androidx.constraintlayout.motion.widget.MotionScene;
import androidx.constraintlayout.widget.ConstraintAttribute.AttributeType;
import androidx.constraintlayout.widget.ConstraintLayout.LayoutParams;

import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;

/**
 * Defines a set of constraints to be used with {@link ConstraintLayout}.
 *
 * <p>{@code ConstraintSet} enables you create and save constraints and apply
 * them to an existing {@code ConstraintLayout}. For details about constraint
 * behaviour, see {@link ConstraintLayout}.</p>
 *
 * <p>{@code ConstraintsSet} can be created in various ways:</p>
 * <ul>
 *     <li>Manually &mdash;
 *         {@code c = new ConstraintSet(); c.connect(...);}</li>
 *     <li>From an {@code R.layout.*} object &mdash;
 *         {@code c.clone(context, R.layout.layout1);}</li>
 *     <li>From a {@code ConstraintLayout} &mdash;
 *         {@code c.clone(constraintLayout);}</li>
 * </ul>
 *
 * <p>Example code:</p>
 * <pre>import android.content.Context;
 *      import android.os.Bundle;
 *      import android.support.constraint.ConstraintLayout;
 *      import android.support.constraint.ConstraintSet;
 *      import android.support.transition.TransitionManager;
 *      import android.support.v7.app.AppCompatActivity;
 *      import android.view.View;
 *
 *      public class MainActivity extends AppCompatActivity {
 *          ConstraintSet mConstraintSet1 = new ConstraintSet(); // Create a ConstraintSet.
 *          ConstraintSet mConstraintSet2 = new ConstraintSet(); // Create a ConstraintSet.
 *          ConstraintLayout mConstraintLayout; // Cache the ConstraintLayout.
 *          boolean mOld = true;
 *
 *
 *          protected void onCreate(Bundle savedInstanceState) {
 *              super.onCreate(savedInstanceState);
 *              Context context = this;
 *              mConstraintSet2.clone(context, R.layout.state2); // Get constraints from layout.
 *              setContentView(R.layout.state1);
 *              mConstraintLayout = (ConstraintLayout) findViewById(R.id.activity_main);
 *              mConstraintSet1.clone(mConstraintLayout); // Get constraints from ConstraintSet.
 *          }
 *
 *          public void foo(View view) {
 *              TransitionManager.beginDelayedTransition(mConstraintLayout);
 *              if (mOld = !mOld) {
 *                  mConstraintSet1.applyTo(mConstraintLayout); // Set new constraints.
 *              }  else {
 *                  mConstraintSet2.applyTo(mConstraintLayout); // Set new constraints.
 *              }
 *          }
 *      }</pre>
 */
public class ConstraintSet {
    private static final String TAG = "ConstraintSet";
    private static final String ERROR_MESSAGE = "XML parser error must be within a Constraint ";

    private static final int INTERNAL_MATCH_PARENT = -1;
    private static final int INTERNAL_WRAP_CONTENT = -2;
    private static final int INTERNAL_MATCH_CONSTRAINT = -3;
    private static final int INTERNAL_WRAP_CONTENT_CONSTRAINED = -4;

    private boolean mValidate;
    public String mIdString;
    public String derivedState = "";
    private String [] mMatchLabels = new String[0];
    public static final int ROTATE_NONE = 0;
    public static final int ROTATE_PORTRATE_OF_RIGHT = 1;
    public static final int ROTATE_PORTRATE_OF_LEFT = 2;
    public static final int ROTATE_RIGHT_OF_PORTRATE = 3;
    public static final int ROTATE_LEFT_OF_PORTRATE = 4;
    public int mRotate = 0;
    private HashMap<String, ConstraintAttribute> mSavedAttributes = new HashMap<>();

    /**
     * require that all views have IDs to function
     */
    private boolean mForceId = true;
    /**
     * Used to indicate a parameter is cleared or not set
     */
    public static final int UNSET = LayoutParams.UNSET;

    /**
     * Dimension will be controlled by constraints
     */
    public static final int MATCH_CONSTRAINT = ConstraintLayout.LayoutParams.MATCH_CONSTRAINT;

    /**
     * Dimension will set by the view's content
     */
    public static final int WRAP_CONTENT = ConstraintLayout.LayoutParams.WRAP_CONTENT;

    /**
     * How to calculate the size of a view in 0 dp by using its wrap_content size
     */
    public static final int MATCH_CONSTRAINT_WRAP =
            ConstraintLayout.LayoutParams.MATCH_CONSTRAINT_WRAP;

    /**
     * Calculate the size of a view in 0 dp by reducing the constrains gaps as much as possible
     */
    public static final int MATCH_CONSTRAINT_SPREAD =
            ConstraintLayout.LayoutParams.MATCH_CONSTRAINT_SPREAD;

    public static final int MATCH_CONSTRAINT_PERCENT =
            ConstraintLayout.LayoutParams.MATCH_CONSTRAINT_PERCENT;

    /**
     * References the id of the parent.
     * Used in:
     * <ul>
     * <li>{@link #connect(int, int, int, int, int)}</li>
     * <li>{@link #center(int, int, int, int, int, int, int, float)}</li>
     * </ul>
     */
    public static final int PARENT_ID = ConstraintLayout.LayoutParams.PARENT_ID;

    /**
     * The horizontal orientation.
     */
    public static final int HORIZONTAL = ConstraintLayout.LayoutParams.HORIZONTAL;

    /**
     * The vertical orientation.
     */
    public static final int VERTICAL = ConstraintLayout.LayoutParams.VERTICAL;

    /**
     * Used to create a horizontal create guidelines.
     */
    public static final int HORIZONTAL_GUIDELINE = 0;

    /**
     * Used to create a vertical create guidelines.
     * see {@link #create(int, int)}
     */
    public static final int VERTICAL_GUIDELINE = 1;

    /**
     * This view is visible.
     * Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code
     * android:visibility}.
     */
    public static final int VISIBLE = View.VISIBLE;

    /**
     * This view is invisible, but it still takes up space for layout purposes.
     * Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code
     * android:visibility}.
     */
    public static final int INVISIBLE = View.INVISIBLE;

    /**
     * This view is gone, and will not take any space for layout
     * purposes. Use with {@link #setVisibility} and <a href="#attr_android:visibility">{@code
     * android:visibility}.
     */
    public static final int GONE = View.GONE;

    /**
     * The left side of a view.
     */
    public static final int LEFT = ConstraintLayout.LayoutParams.LEFT;

    /**
     * The right side of a view.
     */
    public static final int RIGHT = ConstraintLayout.LayoutParams.RIGHT;

    /**
     * The top of a view.
     */
    public static final int TOP = ConstraintLayout.LayoutParams.TOP;

    /**
     * The bottom side of a view.
     */
    public static final int BOTTOM = ConstraintLayout.LayoutParams.BOTTOM;

    /**
     * The baseline of the text in a view.
     */
    public static final int BASELINE = ConstraintLayout.LayoutParams.BASELINE;

    /**
     * The left side of a view in left to right languages.
     * In right to left languages it corresponds to the right side of the view
     */
    public static final int START = ConstraintLayout.LayoutParams.START;

    /**
     * The right side of a view in left to right languages.
     * In right to left languages it corresponds to the left side of the view
     */
    public static final int END = ConstraintLayout.LayoutParams.END;

    /**
     * Circle reference from a view.
     */
    public static final int CIRCLE_REFERENCE = ConstraintLayout.LayoutParams.CIRCLE;

    /**
     * Chain spread style
     */
    public static final int CHAIN_SPREAD = ConstraintLayout.LayoutParams.CHAIN_SPREAD;

    /**
     * Chain spread inside style
     */
    public static final int CHAIN_SPREAD_INSIDE = ConstraintLayout.LayoutParams.CHAIN_SPREAD_INSIDE;

    public static final int VISIBILITY_MODE_NORMAL = 0;
    public static final int VISIBILITY_MODE_IGNORE = 1;
    /**
     * Chain packed style
     */
    public static final int CHAIN_PACKED = ConstraintLayout.LayoutParams.CHAIN_PACKED;

    private static final boolean DEBUG = false;
    private static final int[] VISIBILITY_FLAGS = new int[]{VISIBLE, INVISIBLE, GONE};
    private static final int BARRIER_TYPE = 1;

    private HashMap<Integer, Constraint> mConstraints = new HashMap<Integer, Constraint>();

    private static SparseIntArray sMapToConstant = new SparseIntArray();
    private static SparseIntArray sOverrideMapToConstant = new SparseIntArray();
    private static final int BASELINE_TO_BASELINE = 1;
    private static final int BOTTOM_MARGIN = 2;
    private static final int BOTTOM_TO_BOTTOM = 3;
    private static final int BOTTOM_TO_TOP = 4;
    private static final int DIMENSION_RATIO = 5;
    private static final int EDITOR_ABSOLUTE_X = 6;
    private static final int EDITOR_ABSOLUTE_Y = 7;
    private static final int END_MARGIN = 8;
    private static final int END_TO_END = 9;
    private static final int END_TO_START = 10;
    private static final int GONE_BOTTOM_MARGIN = 11;
    private static final int GONE_END_MARGIN = 12;
    private static final int GONE_LEFT_MARGIN = 13;
    private static final int GONE_RIGHT_MARGIN = 14;
    private static final int GONE_START_MARGIN = 15;
    private static final int GONE_TOP_MARGIN = 16;
    private static final int GUIDE_BEGIN = 17;
    private static final int GUIDE_END = 18;
    private static final int GUIDE_PERCENT = 19;
    private static final int HORIZONTAL_BIAS = 20;
    private static final int LAYOUT_HEIGHT = 21;
    private static final int LAYOUT_VISIBILITY = 22;
    private static final int LAYOUT_WIDTH = 23;
    private static final int LEFT_MARGIN = 24;
    private static final int LEFT_TO_LEFT = 25;
    private static final int LEFT_TO_RIGHT = 26;
    private static final int ORIENTATION = 27;
    private static final int RIGHT_MARGIN = 28;
    private static final int RIGHT_TO_LEFT = 29;
    private static final int RIGHT_TO_RIGHT = 30;
    private static final int START_MARGIN = 31;
    private static final int START_TO_END = 32;
    private static final int START_TO_START = 33;
    private static final int TOP_MARGIN = 34;
    private static final int TOP_TO_BOTTOM = 35;
    private static final int TOP_TO_TOP = 36;
    private static final int VERTICAL_BIAS = 37;
    private static final int VIEW_ID = 38;
    private static final int HORIZONTAL_WEIGHT = 39;
    private static final int VERTICAL_WEIGHT = 40;
    private static final int HORIZONTAL_STYLE = 41;
    private static final int VERTICAL_STYLE = 42;
    private static final int ALPHA = 43;
    private static final int ELEVATION = 44;
    private static final int ROTATION_X = 45;
    private static final int ROTATION_Y = 46;
    private static final int SCALE_X = 47;
    private static final int SCALE_Y = 48;
    private static final int TRANSFORM_PIVOT_X = 49;
    private static final int TRANSFORM_PIVOT_Y = 50;
    private static final int TRANSLATION_X = 51;
    private static final int TRANSLATION_Y = 52;
    private static final int TRANSLATION_Z = 53;
    private static final int WIDTH_DEFAULT = 54;
    private static final int HEIGHT_DEFAULT = 55;
    private static final int WIDTH_MAX = 56;
    private static final int HEIGHT_MAX = 57;
    private static final int WIDTH_MIN = 58;
    private static final int HEIGHT_MIN = 59;
    private static final int ROTATION = 60;
    private static final int CIRCLE = 61;
    private static final int CIRCLE_RADIUS = 62;
    private static final int CIRCLE_ANGLE = 63;
    private static final int ANIMATE_RELATIVE_TO = 64;
    private static final int TRANSITION_EASING = 65;
    private static final int DRAW_PATH = 66;
    private static final int TRANSITION_PATH_ROTATE = 67;
    private static final int PROGRESS = 68;
    private static final int WIDTH_PERCENT = 69;
    private static final int HEIGHT_PERCENT = 70;
    private static final int CHAIN_USE_RTL = 71;
    private static final int BARRIER_DIRECTION = 72;
    private static final int BARRIER_MARGIN = 73;
    private static final int CONSTRAINT_REFERENCED_IDS = 74;
    private static final int BARRIER_ALLOWS_GONE_WIDGETS = 75;
    private static final int PATH_MOTION_ARC = 76;
    private static final int CONSTRAINT_TAG = 77;
    private static final int VISIBILITY_MODE = 78;
    private static final int MOTION_STAGGER = 79;
    private static final int CONSTRAINED_WIDTH = 80;
    private static final int CONSTRAINED_HEIGHT = 81;
    private static final int ANIMATE_CIRCLE_ANGLE_TO = 82;
    private static final int TRANSFORM_PIVOT_TARGET = 83;
    private static final int QUANTIZE_MOTION_STEPS = 84;
    private static final int QUANTIZE_MOTION_PHASE = 85;
    private static final int QUANTIZE_MOTION_INTERPOLATOR = 86;
    private static final int UNUSED = 87;
    private static final int QUANTIZE_MOTION_INTERPOLATOR_TYPE = 88;
    private static final int QUANTIZE_MOTION_INTERPOLATOR_ID = 89;
    private static final int QUANTIZE_MOTION_INTERPOLATOR_STR = 90;
    private static final int BASELINE_TO_TOP = 91;
    private static final int BASELINE_TO_BOTTOM = 92;
    private static final int BASELINE_MARGIN = 93;
    private static final int GONE_BASELINE_MARGIN = 94;
    private static final int LAYOUT_CONSTRAINT_WIDTH = 95;
    private static final int LAYOUT_CONSTRAINT_HEIGHT = 96;
    private static final int LAYOUT_WRAP_BEHAVIOR = 97;
    private static final int MOTION_TARGET = 98;
    private static final int GUIDELINE_USE_RTL = 99;

    private static final String KEY_WEIGHT = "weight";
    private static final String KEY_RATIO = "ratio";
    private static final String KEY_PERCENT_PARENT = "parent";


    static {
        sMapToConstant.append(R.styleable.Constraint_layout_constraintLeft_toLeftOf, LEFT_TO_LEFT);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintLeft_toRightOf,
                LEFT_TO_RIGHT);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintRight_toLeftOf,
                RIGHT_TO_LEFT);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintRight_toRightOf, RIGHT_TO_RIGHT);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintTop_toTopOf, TOP_TO_TOP);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintTop_toBottomOf,
                TOP_TO_BOTTOM);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintBottom_toTopOf,
                BOTTOM_TO_TOP);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintBottom_toBottomOf, BOTTOM_TO_BOTTOM);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintBaseline_toBaselineOf,
                BASELINE_TO_BASELINE);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintBaseline_toTopOf, BASELINE_TO_TOP);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintBaseline_toBottomOf, BASELINE_TO_BOTTOM);

        sMapToConstant.append(R.styleable.Constraint_layout_editor_absoluteX, EDITOR_ABSOLUTE_X);
        sMapToConstant.append(R.styleable.Constraint_layout_editor_absoluteY, EDITOR_ABSOLUTE_Y);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintGuide_begin, GUIDE_BEGIN);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintGuide_end, GUIDE_END);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintGuide_percent, GUIDE_PERCENT);
        sMapToConstant.append(R.styleable.Constraint_guidelineUseRtl, GUIDELINE_USE_RTL);

        sMapToConstant.append(R.styleable.Constraint_android_orientation, ORIENTATION);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintStart_toEndOf, START_TO_END);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintStart_toStartOf, START_TO_START);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintEnd_toStartOf, END_TO_START);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintEnd_toEndOf, END_TO_END);
        sMapToConstant.append(R.styleable.Constraint_layout_goneMarginLeft, GONE_LEFT_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_layout_goneMarginTop, GONE_TOP_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_layout_goneMarginRight, GONE_RIGHT_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_layout_goneMarginBottom, GONE_BOTTOM_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_layout_goneMarginStart, GONE_START_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_layout_goneMarginEnd, GONE_END_MARGIN);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintVertical_weight, VERTICAL_WEIGHT);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintHorizontal_weight, HORIZONTAL_WEIGHT);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintHorizontal_chainStyle, HORIZONTAL_STYLE);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintVertical_chainStyle, VERTICAL_STYLE);

        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintHorizontal_bias, HORIZONTAL_BIAS);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintVertical_bias, VERTICAL_BIAS);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintDimensionRatio, DIMENSION_RATIO);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintLeft_creator, UNUSED);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintTop_creator, UNUSED);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintRight_creator, UNUSED);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintBottom_creator, UNUSED);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintBaseline_creator, UNUSED);
        sMapToConstant.append(R.styleable.Constraint_android_layout_marginLeft, LEFT_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_android_layout_marginRight, RIGHT_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_android_layout_marginStart, START_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_android_layout_marginEnd, END_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_android_layout_marginTop, TOP_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_android_layout_marginBottom, BOTTOM_MARGIN);
        sMapToConstant.append(R.styleable.Constraint_android_layout_width, LAYOUT_WIDTH);
        sMapToConstant.append(
                R.styleable.Constraint_android_layout_height, LAYOUT_HEIGHT);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintWidth, LAYOUT_CONSTRAINT_WIDTH);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintHeight, LAYOUT_CONSTRAINT_HEIGHT);
        sMapToConstant.append(R.styleable.Constraint_android_visibility, LAYOUT_VISIBILITY);
        sMapToConstant.append(R.styleable.Constraint_android_alpha, ALPHA);
        sMapToConstant.append(R.styleable.Constraint_android_elevation, ELEVATION);
        sMapToConstant.append(R.styleable.Constraint_android_rotationX, ROTATION_X);
        sMapToConstant.append(R.styleable.Constraint_android_rotationY, ROTATION_Y);
        sMapToConstant.append(R.styleable.Constraint_android_rotation, ROTATION);
        sMapToConstant.append(R.styleable.Constraint_android_scaleX, SCALE_X);
        sMapToConstant.append(R.styleable.Constraint_android_scaleY, SCALE_Y);
        sMapToConstant.append(R.styleable.Constraint_android_transformPivotX, TRANSFORM_PIVOT_X);
        sMapToConstant.append(R.styleable.Constraint_android_transformPivotY, TRANSFORM_PIVOT_Y);
        sMapToConstant.append(R.styleable.Constraint_android_translationX, TRANSLATION_X);
        sMapToConstant.append(R.styleable.Constraint_android_translationY, TRANSLATION_Y);
        sMapToConstant.append(R.styleable.Constraint_android_translationZ, TRANSLATION_Z);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintWidth_default, WIDTH_DEFAULT);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintHeight_default, HEIGHT_DEFAULT);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintWidth_max, WIDTH_MAX);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintHeight_max, HEIGHT_MAX);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintWidth_min, WIDTH_MIN);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintHeight_min, HEIGHT_MIN);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintCircle, CIRCLE);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintCircleRadius, CIRCLE_RADIUS);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintCircleAngle, CIRCLE_ANGLE);
        sMapToConstant.append(R.styleable.Constraint_animateRelativeTo, ANIMATE_RELATIVE_TO);
        sMapToConstant.append(R.styleable.Constraint_transitionEasing, TRANSITION_EASING);
        sMapToConstant.append(R.styleable.Constraint_drawPath, DRAW_PATH);
        sMapToConstant.append(R.styleable.Constraint_transitionPathRotate, TRANSITION_PATH_ROTATE);
        sMapToConstant.append(R.styleable.Constraint_motionStagger, MOTION_STAGGER);
        sMapToConstant.append(R.styleable.Constraint_android_id, VIEW_ID);
        sMapToConstant.append(R.styleable.Constraint_motionProgress, PROGRESS);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintWidth_percent, WIDTH_PERCENT);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constraintHeight_percent, HEIGHT_PERCENT);
        sMapToConstant.append(
                R.styleable.Constraint_layout_wrapBehaviorInParent, LAYOUT_WRAP_BEHAVIOR);

        sMapToConstant.append(R.styleable.Constraint_chainUseRtl, CHAIN_USE_RTL);
        sMapToConstant.append(R.styleable.Constraint_barrierDirection, BARRIER_DIRECTION);
        sMapToConstant.append(R.styleable.Constraint_barrierMargin, BARRIER_MARGIN);
        sMapToConstant.append(
                R.styleable.Constraint_constraint_referenced_ids, CONSTRAINT_REFERENCED_IDS);
        sMapToConstant.append(
                R.styleable.Constraint_barrierAllowsGoneWidgets, BARRIER_ALLOWS_GONE_WIDGETS);
        sMapToConstant.append(R.styleable.Constraint_pathMotionArc, PATH_MOTION_ARC);
        sMapToConstant.append(R.styleable.Constraint_layout_constraintTag, CONSTRAINT_TAG);
        sMapToConstant.append(R.styleable.Constraint_visibilityMode, VISIBILITY_MODE);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constrainedWidth, CONSTRAINED_WIDTH);
        sMapToConstant.append(
                R.styleable.Constraint_layout_constrainedHeight, CONSTRAINED_HEIGHT);
        sMapToConstant.append(
                R.styleable.Constraint_polarRelativeTo, ANIMATE_CIRCLE_ANGLE_TO);
        sMapToConstant.append(
                R.styleable.Constraint_transformPivotTarget, TRANSFORM_PIVOT_TARGET);
        sMapToConstant.append(
                R.styleable.Constraint_quantizeMotionSteps, QUANTIZE_MOTION_STEPS);
        sMapToConstant.append(
                R.styleable.Constraint_quantizeMotionPhase, QUANTIZE_MOTION_PHASE);
        sMapToConstant.append(
                R.styleable.Constraint_quantizeMotionInterpolator, QUANTIZE_MOTION_INTERPOLATOR);


        /*
        The tags not available in constraintOverride
        Left here to help with documentation and understanding
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintLeft_toLeftOf, LEFT_TO_LEFT);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintLeft_toRightOf, LEFT_TO_RIGHT);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintRight_toLeftOf, RIGHT_TO_LEFT);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintRight_toRightOf, RIGHT_TO_RIGHT);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintTop_toTopOf, TOP_TO_TOP);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintTop_toBottomOf, TOP_TO_BOTTOM);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintBottom_toTopOf, BOTTOM_TO_TOP);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintBottom_toBottomOf, BOTTOM_TO_BOTTOM);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintBaseline_toBaselineOf,
        BASELINE_TO_BASELINE);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintGuide_begin, GUIDE_BEGIN);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintGuide_end, GUIDE_END);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintGuide_percent, GUIDE_PERCENT);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintStart_toEndOf, START_TO_END);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintStart_toStartOf, START_TO_START);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintEnd_toStartOf, END_TO_START);
        overrideMapToConstant.append(
        R.styleable.ConstraintOverride_layout_constraintEnd_toEndOf, END_TO_END);
        */
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_editor_absoluteY, EDITOR_ABSOLUTE_X);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_editor_absoluteY, EDITOR_ABSOLUTE_Y);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_android_orientation, ORIENTATION);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_goneMarginLeft, GONE_LEFT_MARGIN);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_goneMarginTop, GONE_TOP_MARGIN);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_goneMarginRight, GONE_RIGHT_MARGIN);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_goneMarginBottom, GONE_BOTTOM_MARGIN);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_goneMarginStart, GONE_START_MARGIN);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_goneMarginEnd, GONE_END_MARGIN);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintVertical_weight,
                VERTICAL_WEIGHT);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintHorizontal_weight,
                HORIZONTAL_WEIGHT);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintHorizontal_chainStyle,
                HORIZONTAL_STYLE);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintVertical_chainStyle,
                VERTICAL_STYLE);

        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintHorizontal_bias,
                HORIZONTAL_BIAS);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintVertical_bias,
                VERTICAL_BIAS);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintDimensionRatio, DIMENSION_RATIO);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintLeft_creator,
                UNUSED);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintTop_creator,
                UNUSED);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintRight_creator,
                UNUSED);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintBottom_creator, UNUSED);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintBaseline_creator,
                UNUSED);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_marginLeft,
                LEFT_MARGIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_marginRight,
                RIGHT_MARGIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_marginStart,
                START_MARGIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_marginEnd,
                END_MARGIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_marginTop,
                TOP_MARGIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_marginBottom,
                BOTTOM_MARGIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_width,
                LAYOUT_WIDTH);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_layout_height,
                LAYOUT_HEIGHT);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintWidth,
                LAYOUT_CONSTRAINT_WIDTH);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintHeight,
                LAYOUT_CONSTRAINT_HEIGHT);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_visibility,
                LAYOUT_VISIBILITY);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_alpha, ALPHA);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_elevation, ELEVATION);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_rotationX, ROTATION_X);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_rotationY, ROTATION_Y);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_rotation, ROTATION);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_scaleX, SCALE_X);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_scaleY, SCALE_Y);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_transformPivotX,
                TRANSFORM_PIVOT_X);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_transformPivotY,
                TRANSFORM_PIVOT_Y);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_translationX,
                TRANSLATION_X);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_translationY,
                TRANSLATION_Y);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_translationZ,
                TRANSLATION_Z);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintWidth_default,
                WIDTH_DEFAULT);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintHeight_default, HEIGHT_DEFAULT);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintWidth_max,
                WIDTH_MAX);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintHeight_max,
                HEIGHT_MAX);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintWidth_min,
                WIDTH_MIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintHeight_min,
                HEIGHT_MIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintCircleRadius,
                CIRCLE_RADIUS);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintCircleAngle,
                CIRCLE_ANGLE);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_animateRelativeTo,
                ANIMATE_RELATIVE_TO);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_transitionEasing,
                TRANSITION_EASING);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_drawPath, DRAW_PATH);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_transitionPathRotate,
                TRANSITION_PATH_ROTATE);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_motionStagger, MOTION_STAGGER);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_android_id, VIEW_ID);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_motionTarget, MOTION_TARGET);

        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_motionProgress, PROGRESS);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintWidth_percent,
                WIDTH_PERCENT);
        sOverrideMapToConstant.append(
                R.styleable.ConstraintOverride_layout_constraintHeight_percent, HEIGHT_PERCENT);

        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_chainUseRtl, CHAIN_USE_RTL);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_barrierDirection,
                BARRIER_DIRECTION);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_barrierMargin,
                BARRIER_MARGIN);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_constraint_referenced_ids,
                CONSTRAINT_REFERENCED_IDS);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_barrierAllowsGoneWidgets,
                BARRIER_ALLOWS_GONE_WIDGETS);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_pathMotionArc,
                PATH_MOTION_ARC);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constraintTag,
                CONSTRAINT_TAG);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_visibilityMode,
                VISIBILITY_MODE);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constrainedWidth,
                CONSTRAINED_WIDTH);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_constrainedHeight,
                CONSTRAINED_HEIGHT);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_polarRelativeTo,
                ANIMATE_CIRCLE_ANGLE_TO);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_transformPivotTarget,
                TRANSFORM_PIVOT_TARGET);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_quantizeMotionSteps,
                QUANTIZE_MOTION_STEPS);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_quantizeMotionPhase,
                QUANTIZE_MOTION_PHASE);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_quantizeMotionInterpolator,
                QUANTIZE_MOTION_INTERPOLATOR);
        sOverrideMapToConstant.append(R.styleable.ConstraintOverride_layout_wrapBehaviorInParent,
                LAYOUT_WRAP_BEHAVIOR);

    }

    public HashMap<String, ConstraintAttribute> getCustomAttributeSet() {
        return mSavedAttributes;
    }

    // @TODO: add description

    /**
     *
     * @param mId
     * @return
     */
    public Constraint getParameters(int mId) {
        return get(mId);
    }

    /**
     * This will copy Constraints from the ConstraintSet
     *
     * @param set
     */
    public void readFallback(ConstraintSet set) {

        for (Integer key : set.mConstraints.keySet()) {
            int id = key;
            Constraint parent = set.mConstraints.get(key);

            if (!mConstraints.containsKey(id)) {
                mConstraints.put(id, new Constraint());
            }
            Constraint constraint = mConstraints.get(id);
            if (constraint == null) {
                continue;
            }
            if (!constraint.layout.mApply) {
                constraint.layout.copyFrom(parent.layout);
            }
            if (!constraint.propertySet.mApply) {
                constraint.propertySet.copyFrom(parent.propertySet);
            }
            if (!constraint.transform.mApply) {
                constraint.transform.copyFrom(parent.transform);
            }
            if (!constraint.motion.mApply) {
                constraint.motion.copyFrom(parent.motion);
            }
            for (String s : parent.mCustomConstraints.keySet()) {
                if (!constraint.mCustomConstraints.containsKey(s)) {
                    constraint.mCustomConstraints.put(s, parent.mCustomConstraints.get(s));
                }
            }
        }
    }

    /**
     * This will copy Constraints from the ConstraintLayout if it does not have parameters
     *
     * @param constraintLayout
     */
    public void readFallback(ConstraintLayout constraintLayout) {
        int count = constraintLayout.getChildCount();
        for (int i = 0; i < count; i++) {
            View view = constraintLayout.getChildAt(i);
            ConstraintLayout.LayoutParams param =
                    (ConstraintLayout.LayoutParams) view.getLayoutParams();

            int id = view.getId();
            if (mForceId && id == -1) {
                throw new RuntimeException("All children of ConstraintLayout "
                        + "must have ids to use ConstraintSet");
            }
            if (!mConstraints.containsKey(id)) {
                mConstraints.put(id, new Constraint());
            }
            Constraint constraint = mConstraints.get(id);
            if (constraint == null) {
                continue;
            }
            if (!constraint.layout.mApply) {
                constraint.fillFrom(id, param);
                if (view instanceof ConstraintHelper) {
                    constraint.layout.mReferenceIds = ((ConstraintHelper) view).getReferencedIds();
                    if (view instanceof Barrier) {
                        Barrier barrier = (Barrier) view;
                        constraint.layout.mBarrierAllowsGoneWidgets = barrier.getAllowsGoneWidget();
                        constraint.layout.mBarrierDirection = barrier.getType();
                        constraint.layout.mBarrierMargin = barrier.getMargin();
                    }
                }
                constraint.layout.mApply = true;
            }
            if (!constraint.propertySet.mApply) {
                constraint.propertySet.visibility = view.getVisibility();
                constraint.propertySet.alpha = view.getAlpha();
                constraint.propertySet.mApply = true;
            }
            if (!constraint.transform.mApply) {
                constraint.transform.mApply = true;
                constraint.transform.rotation = view.getRotation();
                constraint.transform.rotationX = view.getRotationX();
                constraint.transform.rotationY = view.getRotationY();
                constraint.transform.scaleX = view.getScaleX();
                constraint.transform.scaleY = view.getScaleY();

                float pivotX = view.getPivotX(); // we assume it is not set if set to 0.0
                float pivotY = view.getPivotY(); // we assume it is not set if set to 0.0

                if (pivotX != 0.0 || pivotY != 0.0) {
                    constraint.transform.transformPivotX = pivotX;
                    constraint.transform.transformPivotY = pivotY;
                }

                constraint.transform.translationX = view.getTranslationX();
                constraint.transform.translationY = view.getTranslationY();
                if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                    constraint.transform.translationZ = view.getTranslationZ();
                    if (constraint.transform.applyElevation) {
                        constraint.transform.elevation = view.getElevation();
                    }
                }
            }
        }

    }

    /**
     * Get the delta form the ConstraintSet and aplly to this
     * @param cs
     */
    public void applyDeltaFrom(ConstraintSet cs) {
        for (Constraint from : cs.mConstraints.values()) {
            if (from.mDelta == null) {
                continue;
            }
            if (from.mTargetString == null) {
                Constraint constraint = getConstraint(from.mViewId);
                from.mDelta.applyDelta(constraint);
                continue;
            }
            for (int key : mConstraints.keySet()) {
                Constraint potential = getConstraint(key);
                if (potential.layout.mConstraintTag == null) {
                    continue;
                }
                if (from.mTargetString.matches(potential.layout.mConstraintTag)) {
                    from.mDelta.applyDelta(potential);

                    @SuppressWarnings("unchecked")
                    HashMap<String, ConstraintAttribute> fromClone =
                            (HashMap<String, ConstraintAttribute>) from.mCustomConstraints.clone();
                    potential.mCustomConstraints.putAll(fromClone);
                }
            }
        }
    }

    /**
     * Parse the constraint dimension attribute
     *
     * @param a
     * @param attr
     * @param orientation
     */
    static void parseDimensionConstraints(Object data, TypedArray a, int attr, int orientation) {
        if (data == null) {
            return;
        }
        // data can be of:
        //
        // ConstraintLayout.LayoutParams
        // ConstraintSet.Layout
        // Constraint.Delta

        TypedValue v = a.peekValue(attr);
        int type = v.type;
        int finalValue = 0;
        boolean finalConstrained = false;
        switch (type) {
            case TypedValue.TYPE_DIMENSION: {
                finalValue = a.getDimensionPixelSize(attr, 0);
            }
            break;
            case TypedValue.TYPE_STRING: {
                String value = a.getString(attr);
                parseDimensionConstraintsString(data, value, orientation);
                return;
            }
            default: {
                int value = a.getInt(attr, 0);
                switch (value) {
                    case INTERNAL_WRAP_CONTENT:
                    case INTERNAL_MATCH_PARENT: {
                        finalValue = value;
                    }
                        break;
                    case INTERNAL_MATCH_CONSTRAINT: {
                        finalValue = MATCH_CONSTRAINT;
                    }
                    break;
                    case INTERNAL_WRAP_CONTENT_CONSTRAINED: {
                        finalValue = WRAP_CONTENT;
                        finalConstrained = true;
                    }
                    break;
                }
            }
        }

        if (data instanceof ConstraintLayout.LayoutParams) {
            ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) data;
            if (orientation == HORIZONTAL) {
                params.width = finalValue;
                params.constrainedWidth = finalConstrained;
            } else {
                params.height = finalValue;
                params.constrainedHeight = finalConstrained;
            }
        } else if (data instanceof Layout) {
            Layout params = (Layout) data;
            if (orientation == HORIZONTAL) {
                params.mWidth = finalValue;
                params.constrainedWidth = finalConstrained;
            } else {
                params.mHeight = finalValue;
                params.constrainedHeight = finalConstrained;
            }
        } else if (data instanceof Constraint.Delta) {
            Constraint.Delta params = (Constraint.Delta) data;
            if (orientation == HORIZONTAL) {
                params.add(LAYOUT_WIDTH, finalValue);
                params.add(CONSTRAINED_WIDTH, finalConstrained);
            } else {
                params.add(LAYOUT_HEIGHT, finalValue);
                params.add(CONSTRAINED_HEIGHT, finalConstrained);
            }
        }
    }

    /**
     * Parse the dimension ratio string
     *
     * @param value
     */
    static void parseDimensionRatioString(ConstraintLayout.LayoutParams params, String value) {
        String dimensionRatio = value;
        float dimensionRatioValue = Float.NaN;
        int dimensionRatioSide = UNSET;
        if (dimensionRatio != null) {
            int len = dimensionRatio.length();
            int commaIndex = dimensionRatio.indexOf(',');
            if (commaIndex > 0 && commaIndex < len - 1) {
                String dimension = dimensionRatio.substring(0, commaIndex);
                if (dimension.equalsIgnoreCase("W")) {
                    dimensionRatioSide = HORIZONTAL;
                } else if (dimension.equalsIgnoreCase("H")) {
                    dimensionRatioSide = VERTICAL;
                }
                commaIndex++;
            } else {
                commaIndex = 0;
            }
            int colonIndex = dimensionRatio.indexOf(':');
            if (colonIndex >= 0 && colonIndex < len - 1) {
                String nominator = dimensionRatio.substring(commaIndex, colonIndex);
                String denominator = dimensionRatio.substring(colonIndex + 1);
                if (nominator.length() > 0 && denominator.length() > 0) {
                    try {
                        float nominatorValue = Float.parseFloat(nominator);
                        float denominatorValue = Float.parseFloat(denominator);
                        if (nominatorValue > 0 && denominatorValue > 0) {
                            if (dimensionRatioSide == VERTICAL) {
                                dimensionRatioValue = Math.abs(denominatorValue / nominatorValue);
                            } else {
                                dimensionRatioValue = Math.abs(nominatorValue / denominatorValue);
                            }
                        }
                    } catch (NumberFormatException e) {
                        // Ignore
                    }
                }
            } else {
                String r = dimensionRatio.substring(commaIndex);
                if (r.length() > 0) {
                    try {
                        dimensionRatioValue = Float.parseFloat(r);
                    } catch (NumberFormatException e) {
                        // Ignore
                    }
                }
            }
        }
        params.dimensionRatio = dimensionRatio;
        params.mDimensionRatioValue = dimensionRatioValue;
        params.mDimensionRatioSide = dimensionRatioSide;
    }

    /**
     * Parse the constraints string dimension
     *
     * @param value
     * @param orientation
     */
    static void parseDimensionConstraintsString(Object data, String value, int orientation) {
        // data can be of:
        //
        // ConstraintLayout.LayoutParams
        // ConstraintSet.Layout
        // Constraint.Delta

        // String should be of the form
        //
        // "<Key>=<Value>"
        // supported Keys are:
        // "weight=<value>"
        // "ratio=<value>"
        // "parent=<value>"
        if (value == null) {
            return;
        }

        int equalIndex = value.indexOf('=');
        int len = value.length();
        if (equalIndex > 0 && equalIndex < len - 1) {
            String key = value.substring(0, equalIndex);
            String val = value.substring(equalIndex + 1);
            if (val.length() > 0) {
                key = key.trim();
                val = val.trim();
                if (KEY_RATIO.equalsIgnoreCase(key)) {
                    if (data instanceof ConstraintLayout.LayoutParams) {
                        ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) data;
                        if (orientation == HORIZONTAL) {
                            params.width = MATCH_CONSTRAINT;
                        } else {
                            params.height = MATCH_CONSTRAINT;
                        }
                        parseDimensionRatioString(params, val);
                    } else if (data instanceof Layout) {
                        Layout params = (Layout) data;
                        params.dimensionRatio = val;
                    } else if (data instanceof Constraint.Delta) {
                        Constraint.Delta params = (Constraint.Delta) data;
                        params.add(DIMENSION_RATIO, val);
                    }
                } else if (KEY_WEIGHT.equalsIgnoreCase(key)) {
                    try {
                        float weight = Float.parseFloat(val);
                        if (data instanceof ConstraintLayout.LayoutParams) {
                            ConstraintLayout.LayoutParams params =
                                    (ConstraintLayout.LayoutParams) data;
                            if (orientation == HORIZONTAL) {
                                params.width = MATCH_CONSTRAINT;
                                params.horizontalWeight = weight;
                            } else {
                                params.height = MATCH_CONSTRAINT;
                                params.verticalWeight = weight;
                            }
                        } else if (data instanceof Layout) {
                            Layout params = (Layout) data;
                            if (orientation == HORIZONTAL) {
                                params.mWidth = MATCH_CONSTRAINT;
                                params.horizontalWeight = weight;
                            } else {
                                params.mHeight = MATCH_CONSTRAINT;
                                params.verticalWeight = weight;
                            }
                        } else if (data instanceof Constraint.Delta) {
                            Constraint.Delta params = (Constraint.Delta) data;
                            if (orientation == HORIZONTAL) {
                                params.add(LAYOUT_WIDTH, MATCH_CONSTRAINT);
                                params.add(HORIZONTAL_WEIGHT, weight);
                            } else {
                                params.add(LAYOUT_HEIGHT, MATCH_CONSTRAINT);
                                params.add(VERTICAL_WEIGHT, weight);
                            }
                        }
                    } catch (NumberFormatException e) {
                        // nothing
                    }
                } else if (KEY_PERCENT_PARENT.equalsIgnoreCase(key)) {
                    try {
                        float percent = Math.min(1, Float.parseFloat(val));
                        percent = Math.max(0, percent);
                        if (data instanceof ConstraintLayout.LayoutParams) {
                            ConstraintLayout.LayoutParams params =
                                    (ConstraintLayout.LayoutParams) data;
                            if (orientation == HORIZONTAL) {
                                params.width = MATCH_CONSTRAINT;
                                params.matchConstraintPercentWidth = percent;
                                params.matchConstraintDefaultWidth = MATCH_CONSTRAINT_PERCENT;
                            } else {
                                params.height = MATCH_CONSTRAINT;
                                params.matchConstraintPercentHeight = percent;
                                params.matchConstraintDefaultHeight = MATCH_CONSTRAINT_PERCENT;
                            }
                        } else if (data instanceof Layout) {
                            Layout params = (Layout) data;
                            if (orientation == HORIZONTAL) {
                                params.mWidth = MATCH_CONSTRAINT;
                                params.widthPercent = percent;
                                params.widthDefault = MATCH_CONSTRAINT_PERCENT;
                            } else {
                                params.mHeight = MATCH_CONSTRAINT;
                                params.heightPercent = percent;
                                params.heightDefault = MATCH_CONSTRAINT_PERCENT;
                            }
                        } else if (data instanceof Constraint.Delta) {
                            Constraint.Delta params = (Constraint.Delta) data;
                            if (orientation == HORIZONTAL) {
                                params.add(LAYOUT_WIDTH, MATCH_CONSTRAINT);
                                params.add(WIDTH_DEFAULT, MATCH_CONSTRAINT_PERCENT);
                            } else {
                                params.add(LAYOUT_HEIGHT, MATCH_CONSTRAINT);
                                params.add(HEIGHT_DEFAULT, MATCH_CONSTRAINT_PERCENT);
                            }
                        }
                    } catch (NumberFormatException e) {
                        // nothing
                    }
                }
            }
        }
    }


    /**
     * Get the types associated with this ConstraintSet
     * The types mechanism allows you to tag the constraint set
     * with a series of string to define properties of a ConstraintSet
     *
     * @return an array of type strings
     */
    public String[] getStateLabels() {
        return Arrays.copyOf(mMatchLabels, mMatchLabels.length);
    }

    /**
     * Set the types associated with this ConstraintSet
     * The types mechanism allows you to tag the constraint set
     * with a series of string to define properties of a ConstraintSet
     * @param types a comer separated array of strings.
     */
    public void setStateLabels(String types) {
        mMatchLabels = types.split(",");
        for (int i = 0; i < mMatchLabels.length; i++) {
            mMatchLabels[i] = mMatchLabels[i].trim();
        }
    }
    /**
     * Set the types associated with this ConstraintSet
     * The types mechanism allows you to tag the constraint set
     * with a series of string to define properties of a ConstraintSet
     * @param types a comer separated array of strings.
     */
    public void setStateLabelsList(String... types) {
        mMatchLabels = types;
        for (int i = 0; i < mMatchLabels.length; i++) {
            mMatchLabels[i] = mMatchLabels[i].trim();
        }
    }

    /**
     * Test if the list of strings matches labels defined on this constraintSet
     * @param types list of types
     * @return true if all types are in the labels
     */
    public boolean matchesLabels(String...types) {
        for (String type : types) {
            boolean match = false;
            for (String matchType : mMatchLabels) {
                if (matchType.equals(type)) {
                    match = true;
                    break;
                }
            }
            if (!match) {
                return false;
            }

        }
        return true;
    }

    /**
     *
     */
    public static class Layout {
        public boolean mIsGuideline = false;
        public boolean mApply = false;
        public boolean mOverride = false;
        public int mWidth;
        public int mHeight;
        public static final int UNSET = ConstraintSet.UNSET;
        public static final int UNSET_GONE_MARGIN = Integer.MIN_VALUE;
        public int guideBegin = UNSET;
        public int guideEnd = UNSET;
        public float guidePercent = UNSET;
        public boolean guidelineUseRtl = true;
        public int leftToLeft = UNSET;
        public int leftToRight = UNSET;
        public int rightToLeft = UNSET;
        public int rightToRight = UNSET;
        public int topToTop = UNSET;
        public int topToBottom = UNSET;
        public int bottomToTop = UNSET;
        public int bottomToBottom = UNSET;
        public int baselineToBaseline = UNSET;
        public int baselineToTop = UNSET;
        public int baselineToBottom = UNSET;
        public int startToEnd = UNSET;
        public int startToStart = UNSET;
        public int endToStart = UNSET;
        public int endToEnd = UNSET;
        public float horizontalBias = 0.5f;
        public float verticalBias = 0.5f;
        public String dimensionRatio = null;
        public int circleConstraint = UNSET;
        public int circleRadius = 0;
        public float circleAngle = 0;
        public int editorAbsoluteX = UNSET;
        public int editorAbsoluteY = UNSET;
        public int orientation = UNSET;
        public int leftMargin = 0;
        public int rightMargin = 0;
        public int topMargin = 0;
        public int bottomMargin = 0;
        public int endMargin = 0;
        public int startMargin = 0;
        public int baselineMargin = 0;
        public int goneLeftMargin = UNSET_GONE_MARGIN;
        public int goneTopMargin = UNSET_GONE_MARGIN;
        public int goneRightMargin = UNSET_GONE_MARGIN;
        public int goneBottomMargin = UNSET_GONE_MARGIN;
        public int goneEndMargin = UNSET_GONE_MARGIN;
        public int goneStartMargin = UNSET_GONE_MARGIN;
        public int goneBaselineMargin = UNSET_GONE_MARGIN;
        public float verticalWeight = UNSET;
        public float horizontalWeight = UNSET;
        public int horizontalChainStyle = CHAIN_SPREAD;
        public int verticalChainStyle = CHAIN_SPREAD;
        public int widthDefault = ConstraintWidget.MATCH_CONSTRAINT_SPREAD;
        public int heightDefault = ConstraintWidget.MATCH_CONSTRAINT_SPREAD;
        public int widthMax = 0;
        public int heightMax = 0;
        public int widthMin = 0;
        public int heightMin = 0;
        public float widthPercent = 1;
        public float heightPercent = 1;
        public int mBarrierDirection = UNSET;
        public int mBarrierMargin = 0;
        public int mHelperType = UNSET;
        public int[] mReferenceIds;
        public String mReferenceIdString;
        public String mConstraintTag;
        public boolean constrainedWidth = false;
        public boolean constrainedHeight = false;
        // TODO public boolean mChainUseRtl = false;
        public boolean mBarrierAllowsGoneWidgets = true;
        public int mWrapBehavior = ConstraintWidget.WRAP_BEHAVIOR_INCLUDED;

        /**
         * Copy from Layout
         * @param src
         */
        public void copyFrom(Layout src) {
            mIsGuideline = src.mIsGuideline;
            mWidth = src.mWidth;
            mApply = src.mApply;
            mHeight = src.mHeight;
            guideBegin = src.guideBegin;
            guideEnd = src.guideEnd;
            guidePercent = src.guidePercent;
            guidelineUseRtl = src.guidelineUseRtl;
            leftToLeft = src.leftToLeft;
            leftToRight = src.leftToRight;
            rightToLeft = src.rightToLeft;
            rightToRight = src.rightToRight;
            topToTop = src.topToTop;
            topToBottom = src.topToBottom;
            bottomToTop = src.bottomToTop;
            bottomToBottom = src.bottomToBottom;
            baselineToBaseline = src.baselineToBaseline;
            baselineToTop = src.baselineToTop;
            baselineToBottom = src.baselineToBottom;
            startToEnd = src.startToEnd;
            startToStart = src.startToStart;
            endToStart = src.endToStart;
            endToEnd = src.endToEnd;
            horizontalBias = src.horizontalBias;
            verticalBias = src.verticalBias;
            dimensionRatio = src.dimensionRatio;
            circleConstraint = src.circleConstraint;
            circleRadius = src.circleRadius;
            circleAngle = src.circleAngle;
            editorAbsoluteX = src.editorAbsoluteX;
            editorAbsoluteY = src.editorAbsoluteY;
            orientation = src.orientation;
            leftMargin = src.leftMargin;
            rightMargin = src.rightMargin;
            topMargin = src.topMargin;
            bottomMargin = src.bottomMargin;
            endMargin = src.endMargin;
            startMargin = src.startMargin;
            baselineMargin = src.baselineMargin;
            goneLeftMargin = src.goneLeftMargin;
            goneTopMargin = src.goneTopMargin;
            goneRightMargin = src.goneRightMargin;
            goneBottomMargin = src.goneBottomMargin;
            goneEndMargin = src.goneEndMargin;
            goneStartMargin = src.goneStartMargin;
            goneBaselineMargin = src.goneBaselineMargin;
            verticalWeight = src.verticalWeight;
            horizontalWeight = src.horizontalWeight;
            horizontalChainStyle = src.horizontalChainStyle;
            verticalChainStyle = src.verticalChainStyle;
            widthDefault = src.widthDefault;
            heightDefault = src.heightDefault;
            widthMax = src.widthMax;
            heightMax = src.heightMax;
            widthMin = src.widthMin;
            heightMin = src.heightMin;
            widthPercent = src.widthPercent;
            heightPercent = src.heightPercent;
            mBarrierDirection = src.mBarrierDirection;
            mBarrierMargin = src.mBarrierMargin;
            mHelperType = src.mHelperType;
            mConstraintTag = src.mConstraintTag;

            if (src.mReferenceIds != null && src.mReferenceIdString == null) {
                mReferenceIds = Arrays.copyOf(src.mReferenceIds, src.mReferenceIds.length);
            } else {
                mReferenceIds = null;
            }
            mReferenceIdString = src.mReferenceIdString;
            constrainedWidth = src.constrainedWidth;
            constrainedHeight = src.constrainedHeight;
            // TODO mChainUseRtl = t.mChainUseRtl;
            mBarrierAllowsGoneWidgets = src.mBarrierAllowsGoneWidgets;
            mWrapBehavior = src.mWrapBehavior;
        }

        private static SparseIntArray sMapToConstant = new SparseIntArray();
        private static final int BASELINE_TO_BASELINE = 1;
        private static final int BOTTOM_MARGIN = 2;
        private static final int BOTTOM_TO_BOTTOM = 3;
        private static final int BOTTOM_TO_TOP = 4;
        private static final int DIMENSION_RATIO = 5;
        private static final int EDITOR_ABSOLUTE_X = 6;
        private static final int EDITOR_ABSOLUTE_Y = 7;
        private static final int END_MARGIN = 8;
        private static final int END_TO_END = 9;
        private static final int END_TO_START = 10;
        private static final int GONE_BOTTOM_MARGIN = 11;
        private static final int GONE_END_MARGIN = 12;
        private static final int GONE_LEFT_MARGIN = 13;
        private static final int GONE_RIGHT_MARGIN = 14;
        private static final int GONE_START_MARGIN = 15;
        private static final int GONE_TOP_MARGIN = 16;
        private static final int GUIDE_BEGIN = 17;
        private static final int GUIDE_END = 18;
        private static final int GUIDE_PERCENT = 19;
        private static final int HORIZONTAL_BIAS = 20;
        private static final int LAYOUT_HEIGHT = 21;
        private static final int LAYOUT_WIDTH = 22;
        private static final int LEFT_MARGIN = 23;
        private static final int LEFT_TO_LEFT = 24;
        private static final int LEFT_TO_RIGHT = 25;
        private static final int ORIENTATION = 26;
        private static final int RIGHT_MARGIN = 27;
        private static final int RIGHT_TO_LEFT = 28;
        private static final int RIGHT_TO_RIGHT = 29;
        private static final int START_MARGIN = 30;
        private static final int START_TO_END = 31;
        private static final int START_TO_START = 32;
        private static final int TOP_MARGIN = 33;
        private static final int TOP_TO_BOTTOM = 34;
        private static final int TOP_TO_TOP = 35;
        private static final int VERTICAL_BIAS = 36;
        private static final int HORIZONTAL_WEIGHT = 37;
        private static final int VERTICAL_WEIGHT = 38;
        private static final int HORIZONTAL_STYLE = 39;
        private static final int VERTICAL_STYLE = 40;
        private static final int LAYOUT_CONSTRAINT_WIDTH = 41;
        private static final int LAYOUT_CONSTRAINT_HEIGHT = 42;

        private static final int CIRCLE = 61;
        private static final int CIRCLE_RADIUS = 62;
        private static final int CIRCLE_ANGLE = 63;
        private static final int WIDTH_PERCENT = 69;
        private static final int HEIGHT_PERCENT = 70;
        private static final int CHAIN_USE_RTL = 71;
        private static final int BARRIER_DIRECTION = 72;
        private static final int BARRIER_MARGIN = 73;
        private static final int CONSTRAINT_REFERENCED_IDS = 74;
        private static final int BARRIER_ALLOWS_GONE_WIDGETS = 75;

        private static final int LAYOUT_WRAP_BEHAVIOR = 76;
        private static final int BASELINE_TO_TOP = 77;
        private static final int BASELINE_TO_BOTTOM = 78;
        private static final int GONE_BASELINE_MARGIN = 79;
        private static final int BASELINE_MARGIN = 80;
        private static final int WIDTH_DEFAULT = 81;
        private static final int HEIGHT_DEFAULT = 82;
        private static final int HEIGHT_MAX = 83;
        private static final int WIDTH_MAX = 84;
        private static final int HEIGHT_MIN = 85;
        private static final int WIDTH_MIN = 86;
        private static final int CONSTRAINED_WIDTH = 87;
        private static final int CONSTRAINED_HEIGHT = 88;
        private static final int CONSTRAINT_TAG = 89;
        private static final int GUIDE_USE_RTL = 90;

        private static final int UNUSED = 91;

        static {
            sMapToConstant.append(R.styleable.Layout_layout_constraintLeft_toLeftOf, LEFT_TO_LEFT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintLeft_toRightOf,
                    LEFT_TO_RIGHT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintRight_toLeftOf,
                    RIGHT_TO_LEFT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintRight_toRightOf,
                    RIGHT_TO_RIGHT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintTop_toTopOf, TOP_TO_TOP);
            sMapToConstant.append(R.styleable.Layout_layout_constraintTop_toBottomOf,
                    TOP_TO_BOTTOM);
            sMapToConstant.append(R.styleable.Layout_layout_constraintBottom_toTopOf,
                    BOTTOM_TO_TOP);
            sMapToConstant.append(R.styleable.Layout_layout_constraintBottom_toBottomOf,
                    BOTTOM_TO_BOTTOM);
            sMapToConstant.append(R.styleable.Layout_layout_constraintBaseline_toBaselineOf,
                    BASELINE_TO_BASELINE);

            sMapToConstant.append(R.styleable.Layout_layout_editor_absoluteX, EDITOR_ABSOLUTE_X);
            sMapToConstant.append(R.styleable.Layout_layout_editor_absoluteY, EDITOR_ABSOLUTE_Y);
            sMapToConstant.append(R.styleable.Layout_layout_constraintGuide_begin, GUIDE_BEGIN);
            sMapToConstant.append(R.styleable.Layout_layout_constraintGuide_end, GUIDE_END);
            sMapToConstant.append(R.styleable.Layout_layout_constraintGuide_percent, GUIDE_PERCENT);
            sMapToConstant.append(R.styleable.Layout_guidelineUseRtl, GUIDE_USE_RTL);
            sMapToConstant.append(R.styleable.Layout_android_orientation, ORIENTATION);
            sMapToConstant.append(R.styleable.Layout_layout_constraintStart_toEndOf, START_TO_END);
            sMapToConstant.append(R.styleable.Layout_layout_constraintStart_toStartOf,
                    START_TO_START);
            sMapToConstant.append(R.styleable.Layout_layout_constraintEnd_toStartOf, END_TO_START);
            sMapToConstant.append(R.styleable.Layout_layout_constraintEnd_toEndOf, END_TO_END);
            sMapToConstant.append(R.styleable.Layout_layout_goneMarginLeft, GONE_LEFT_MARGIN);
            sMapToConstant.append(R.styleable.Layout_layout_goneMarginTop, GONE_TOP_MARGIN);
            sMapToConstant.append(R.styleable.Layout_layout_goneMarginRight, GONE_RIGHT_MARGIN);
            sMapToConstant.append(R.styleable.Layout_layout_goneMarginBottom, GONE_BOTTOM_MARGIN);
            sMapToConstant.append(R.styleable.Layout_layout_goneMarginStart, GONE_START_MARGIN);
            sMapToConstant.append(R.styleable.Layout_layout_goneMarginEnd, GONE_END_MARGIN);
            sMapToConstant.append(R.styleable.Layout_layout_constraintVertical_weight,
                    VERTICAL_WEIGHT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintHorizontal_weight,
                    HORIZONTAL_WEIGHT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintHorizontal_chainStyle,
                    HORIZONTAL_STYLE);
            sMapToConstant.append(R.styleable.Layout_layout_constraintVertical_chainStyle,
                    VERTICAL_STYLE);

            sMapToConstant.append(R.styleable.Layout_layout_constraintHorizontal_bias,
                    HORIZONTAL_BIAS);
            sMapToConstant.append(R.styleable.Layout_layout_constraintVertical_bias,
                    VERTICAL_BIAS);
            sMapToConstant.append(R.styleable.Layout_layout_constraintDimensionRatio,
                    DIMENSION_RATIO);
            sMapToConstant.append(R.styleable.Layout_layout_constraintLeft_creator, UNUSED);
            sMapToConstant.append(R.styleable.Layout_layout_constraintTop_creator, UNUSED);
            sMapToConstant.append(R.styleable.Layout_layout_constraintRight_creator, UNUSED);
            sMapToConstant.append(R.styleable.Layout_layout_constraintBottom_creator, UNUSED);
            sMapToConstant.append(R.styleable.Layout_layout_constraintBaseline_creator, UNUSED);
            sMapToConstant.append(R.styleable.Layout_android_layout_marginLeft, LEFT_MARGIN);
            sMapToConstant.append(R.styleable.Layout_android_layout_marginRight, RIGHT_MARGIN);
            sMapToConstant.append(R.styleable.Layout_android_layout_marginStart, START_MARGIN);
            sMapToConstant.append(R.styleable.Layout_android_layout_marginEnd, END_MARGIN);
            sMapToConstant.append(R.styleable.Layout_android_layout_marginTop, TOP_MARGIN);
            sMapToConstant.append(R.styleable.Layout_android_layout_marginBottom, BOTTOM_MARGIN);
            sMapToConstant.append(R.styleable.Layout_android_layout_width, LAYOUT_WIDTH);
            sMapToConstant.append(R.styleable.Layout_android_layout_height, LAYOUT_HEIGHT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintWidth,
                    LAYOUT_CONSTRAINT_WIDTH);
            sMapToConstant.append(R.styleable.Layout_layout_constraintHeight,
                    LAYOUT_CONSTRAINT_HEIGHT);
            sMapToConstant.append(R.styleable.Layout_layout_constrainedWidth,
                    CONSTRAINED_WIDTH);
            sMapToConstant.append(R.styleable.Layout_layout_constrainedHeight,
                    CONSTRAINED_HEIGHT);
            sMapToConstant.append(R.styleable.Layout_layout_wrapBehaviorInParent,
                    LAYOUT_WRAP_BEHAVIOR);

            sMapToConstant.append(R.styleable.Layout_layout_constraintCircle, CIRCLE);
            sMapToConstant.append(R.styleable.Layout_layout_constraintCircleRadius, CIRCLE_RADIUS);
            sMapToConstant.append(R.styleable.Layout_layout_constraintCircleAngle, CIRCLE_ANGLE);
            sMapToConstant.append(R.styleable.Layout_layout_constraintWidth_percent, WIDTH_PERCENT);
            sMapToConstant.append(R.styleable.Layout_layout_constraintHeight_percent,
                    HEIGHT_PERCENT);

            sMapToConstant.append(R.styleable.Layout_chainUseRtl, CHAIN_USE_RTL);
            sMapToConstant.append(R.styleable.Layout_barrierDirection, BARRIER_DIRECTION);
            sMapToConstant.append(R.styleable.Layout_barrierMargin, BARRIER_MARGIN);
            sMapToConstant.append(R.styleable.Layout_constraint_referenced_ids,
                    CONSTRAINT_REFERENCED_IDS);
            sMapToConstant.append(R.styleable.Layout_barrierAllowsGoneWidgets,
                    BARRIER_ALLOWS_GONE_WIDGETS);
            sMapToConstant.append(R.styleable.Layout_layout_constraintWidth_max,
                    WIDTH_MAX);
            sMapToConstant.append(R.styleable.Layout_layout_constraintWidth_min,
                    WIDTH_MIN);
            sMapToConstant.append(R.styleable.Layout_layout_constraintWidth_max,
                    HEIGHT_MAX);
            sMapToConstant.append(R.styleable.Layout_layout_constraintHeight_min,
                    HEIGHT_MIN);

            sMapToConstant.append(R.styleable.Layout_layout_constraintWidth, CONSTRAINED_WIDTH);
            sMapToConstant.append(R.styleable.Layout_layout_constraintHeight, CONSTRAINED_HEIGHT);
            sMapToConstant.append(R.styleable.ConstraintLayout_Layout_layout_constraintTag,
                    CONSTRAINT_TAG);
            sMapToConstant.append(R.styleable.Layout_guidelineUseRtl, GUIDE_USE_RTL);

        }

        void fillFromAttributeList(Context context, AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Layout);
            mApply = true;
            final int count = a.getIndexCount();
            for (int i = 0; i < count; i++) {
                int attr = a.getIndex(i);

                switch (sMapToConstant.get(attr)) {
                    case LEFT_TO_LEFT:
                        leftToLeft = lookupID(a, attr, leftToLeft);
                        break;
                    case LEFT_TO_RIGHT:
                        leftToRight = lookupID(a, attr, leftToRight);
                        break;
                    case RIGHT_TO_LEFT:
                        rightToLeft = lookupID(a, attr, rightToLeft);
                        break;
                    case RIGHT_TO_RIGHT:
                        rightToRight = lookupID(a, attr, rightToRight);
                        break;
                    case TOP_TO_TOP:
                        topToTop = lookupID(a, attr, topToTop);
                        break;
                    case TOP_TO_BOTTOM:
                        topToBottom = lookupID(a, attr, topToBottom);
                        break;
                    case BOTTOM_TO_TOP:
                        bottomToTop = lookupID(a, attr, bottomToTop);
                        break;
                    case BOTTOM_TO_BOTTOM:
                        bottomToBottom = lookupID(a, attr, bottomToBottom);
                        break;
                    case BASELINE_TO_BASELINE:
                        baselineToBaseline = lookupID(a, attr, baselineToBaseline);
                        break;
                    case BASELINE_TO_TOP:
                        baselineToTop = lookupID(a, attr, baselineToTop);
                        break;
                    case BASELINE_TO_BOTTOM:
                        baselineToBottom = lookupID(a, attr, baselineToBottom);
                        break;
                    case EDITOR_ABSOLUTE_X:
                        editorAbsoluteX = a.getDimensionPixelOffset(attr, editorAbsoluteX);
                        break;
                    case EDITOR_ABSOLUTE_Y:
                        editorAbsoluteY = a.getDimensionPixelOffset(attr, editorAbsoluteY);
                        break;
                    case GUIDE_BEGIN:
                        guideBegin = a.getDimensionPixelOffset(attr, guideBegin);
                        break;
                    case GUIDE_END:
                        guideEnd = a.getDimensionPixelOffset(attr, guideEnd);
                        break;
                    case GUIDE_PERCENT:
                        guidePercent = a.getFloat(attr, guidePercent);
                        break;
                    case GUIDE_USE_RTL:
                        guidelineUseRtl = a.getBoolean(attr, guidelineUseRtl);
                        break;

                    case ORIENTATION:
                        orientation = a.getInt(attr, orientation);
                        break;
                    case START_TO_END:
                        startToEnd = lookupID(a, attr, startToEnd);
                        break;
                    case START_TO_START:
                        startToStart = lookupID(a, attr, startToStart);
                        break;
                    case END_TO_START:
                        endToStart = lookupID(a, attr, endToStart);
                        break;
                    case END_TO_END:
                        endToEnd = lookupID(a, attr, endToEnd);
                        break;
                    case CIRCLE:
                        circleConstraint = lookupID(a, attr, circleConstraint);
                        break;
                    case CIRCLE_RADIUS:
                        circleRadius = a.getDimensionPixelSize(attr, circleRadius);
                        break;
                    case CIRCLE_ANGLE:
                        circleAngle = a.getFloat(attr, circleAngle);
                        break;
                    case GONE_LEFT_MARGIN:
                        goneLeftMargin = a.getDimensionPixelSize(attr, goneLeftMargin);
                        break;
                    case GONE_TOP_MARGIN:
                        goneTopMargin = a.getDimensionPixelSize(attr, goneTopMargin);
                        break;
                    case GONE_RIGHT_MARGIN:
                        goneRightMargin = a.getDimensionPixelSize(attr, goneRightMargin);
                        break;
                    case GONE_BOTTOM_MARGIN:
                        goneBottomMargin = a.getDimensionPixelSize(attr, goneBottomMargin);
                        break;
                    case GONE_START_MARGIN:
                        goneStartMargin = a.getDimensionPixelSize(attr, goneStartMargin);
                        break;
                    case GONE_END_MARGIN:
                        goneEndMargin = a.getDimensionPixelSize(attr, goneEndMargin);
                        break;
                    case GONE_BASELINE_MARGIN:
                        goneBaselineMargin = a.getDimensionPixelSize(attr, goneBaselineMargin);
                        break;
                    case HORIZONTAL_BIAS:
                        horizontalBias = a.getFloat(attr, horizontalBias);
                        break;
                    case VERTICAL_BIAS:
                        verticalBias = a.getFloat(attr, verticalBias);
                        break;
                    case LEFT_MARGIN:
                        leftMargin = a.getDimensionPixelSize(attr, leftMargin);
                        break;
                    case RIGHT_MARGIN:
                        rightMargin = a.getDimensionPixelSize(attr, rightMargin);
                        break;
                    case START_MARGIN:
                        startMargin = a.getDimensionPixelSize(attr, startMargin);
                        break;
                    case END_MARGIN:
                        endMargin = a.getDimensionPixelSize(attr, endMargin);
                        break;
                    case TOP_MARGIN:
                        topMargin = a.getDimensionPixelSize(attr, topMargin);
                        break;
                    case BOTTOM_MARGIN:
                        bottomMargin = a.getDimensionPixelSize(attr, bottomMargin);
                        break;
                    case BASELINE_MARGIN:
                        baselineMargin = a.getDimensionPixelSize(attr, baselineMargin);
                        break;
                    case LAYOUT_WIDTH:
                        mWidth = a.getLayoutDimension(attr, mWidth);
                        break;
                    case LAYOUT_HEIGHT:
                        mHeight = a.getLayoutDimension(attr, mHeight);
                        break;
                    case LAYOUT_CONSTRAINT_WIDTH:
                        ConstraintSet.parseDimensionConstraints(this, a, attr, HORIZONTAL);
                        break;
                    case LAYOUT_CONSTRAINT_HEIGHT:
                        ConstraintSet.parseDimensionConstraints(this, a, attr, VERTICAL);
                        break;
                    case WIDTH_DEFAULT:
                        widthDefault = a.getInt(attr, widthDefault);
                        break;
                    case HEIGHT_DEFAULT:
                        heightDefault = a.getInt(attr, heightDefault);
                        break;
                    case VERTICAL_WEIGHT:
                        verticalWeight = a.getFloat(attr, verticalWeight);
                        break;
                    case HORIZONTAL_WEIGHT:
                        horizontalWeight = a.getFloat(attr, horizontalWeight);
                        break;
                    case VERTICAL_STYLE:
                        verticalChainStyle = a.getInt(attr, verticalChainStyle);
                        break;
                    case HORIZONTAL_STYLE:
                        horizontalChainStyle = a.getInt(attr, horizontalChainStyle);
                        break;
                    case DIMENSION_RATIO:
                        dimensionRatio = a.getString(attr);
                        break;
                    case HEIGHT_MAX:
                        heightMax = a.getDimensionPixelSize(attr, heightMax);
                        break;
                    case WIDTH_MAX:
                        widthMax = a.getDimensionPixelSize(attr, widthMax);
                        break;
                    case HEIGHT_MIN:
                        heightMin = a.getDimensionPixelSize(attr, heightMin);
                        break;
                    case WIDTH_MIN:
                        widthMin = a.getDimensionPixelSize(attr, widthMin);
                        break;
                    case WIDTH_PERCENT:
                        widthPercent = a.getFloat(attr, 1);
                        break;
                    case HEIGHT_PERCENT:
                        heightPercent = a.getFloat(attr, 1);
                        break;
                    case CONSTRAINED_WIDTH:
                        constrainedWidth = a.getBoolean(attr, constrainedWidth);
                        break;
                    case CONSTRAINED_HEIGHT:
                        constrainedHeight = a.getBoolean(attr, constrainedHeight);
                        break;
                    case CHAIN_USE_RTL:
                        Log.e(TAG, "CURRENTLY UNSUPPORTED"); // TODO add support or remove
                        //  TODO add support or remove  c.mChainUseRtl
                        //   = a.getBoolean(attr,c.mChainUseRtl);
                        break;
                    case BARRIER_DIRECTION:
                        mBarrierDirection = a.getInt(attr, mBarrierDirection);
                        break;
                    case LAYOUT_WRAP_BEHAVIOR:
                        mWrapBehavior = a.getInt(attr, mWrapBehavior);
                        break;
                    case BARRIER_MARGIN:
                        mBarrierMargin = a.getDimensionPixelSize(attr, mBarrierMargin);
                        break;
                    case CONSTRAINT_REFERENCED_IDS:
                        mReferenceIdString = a.getString(attr);
                        break;
                    case BARRIER_ALLOWS_GONE_WIDGETS:
                        mBarrierAllowsGoneWidgets = a.getBoolean(attr, mBarrierAllowsGoneWidgets);
                        break;
                    case CONSTRAINT_TAG:
                        mConstraintTag = a.getString(attr);
                        break;
                    case UNUSED:
                        Log.w(TAG,
                                "unused attribute 0x" + Integer.toHexString(attr)
                                        + "   " + sMapToConstant.get(attr));
                        break;
                    default:
                        Log.w(TAG,
                                "Unknown attribute 0x" + Integer.toHexString(attr)
                                        + "   " + sMapToConstant.get(attr));

                }
            }
            a.recycle();
        }

        /**
         * Print the content to a string
         * @param scene
         * @param stringBuilder
         */
        public void dump(MotionScene scene, StringBuilder stringBuilder) {
            Field[] fields = this.getClass().getDeclaredFields();
            stringBuilder.append("\n");
            for (int i = 0; i < fields.length; i++) {
                Field field = fields[i];
                String name = field.getName();
                if (Modifier.isStatic(field.getModifiers())) {
                    continue;
                }

                try {
                    Object value = field.get(this);
                    Class<?> type = field.getType();
                    if (type == Integer.TYPE) {
                        Integer iValue = (Integer) value;
                        if (iValue != UNSET) {
                            String stringId = scene.lookUpConstraintName(iValue);
                            stringBuilder.append("    ");
                            stringBuilder.append(name);
                            stringBuilder.append(" = \"");
                            stringBuilder.append((stringId == null) ? iValue : stringId);
                            stringBuilder.append("\"\n");
                        }
                    } else if (type == Float.TYPE) {
                        Float fValue = (Float) value;
                        if (fValue != UNSET) {
                            stringBuilder.append("    ");
                            stringBuilder.append(name);
                            stringBuilder.append(" = \"");
                            stringBuilder.append(fValue);
                            stringBuilder.append("\"\n");
                        }
                    }
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "Error accessing ConstraintSet field", e);
                }
            }
        }
    }

    /**
     *
     */
    public static class Transform {
        public boolean mApply = false;
        public float rotation = 0;
        public float rotationX = 0;
        public float rotationY = 0;
        public float scaleX = 1;
        public float scaleY = 1;
        public float transformPivotX = Float.NaN;
        public float transformPivotY = Float.NaN;
        public int transformPivotTarget = UNSET;
        public float translationX = 0;
        public float translationY = 0;
        public float translationZ = 0;
        public boolean applyElevation = false;
        public float elevation = 0;

        /**
         * Copy Transform from src
         * @param src
         */
        public void copyFrom(Transform src) {
            mApply = src.mApply;
            rotation = src.rotation;
            rotationX = src.rotationX;
            rotationY = src.rotationY;
            scaleX = src.scaleX;
            scaleY = src.scaleY;
            transformPivotX = src.transformPivotX;
            transformPivotY = src.transformPivotY;
            transformPivotTarget = src.transformPivotTarget;
            translationX = src.translationX;
            translationY = src.translationY;
            translationZ = src.translationZ;
            applyElevation = src.applyElevation;
            elevation = src.elevation;
        }

        private static SparseIntArray sMapToConstant = new SparseIntArray();
        private static final int ROTATION = 1;
        private static final int ROTATION_X = 2;
        private static final int ROTATION_Y = 3;
        private static final int SCALE_X = 4;
        private static final int SCALE_Y = 5;
        private static final int TRANSFORM_PIVOT_X = 6;
        private static final int TRANSFORM_PIVOT_Y = 7;
        private static final int TRANSLATION_X = 8;
        private static final int TRANSLATION_Y = 9;
        private static final int TRANSLATION_Z = 10;
        private static final int ELEVATION = 11;
        private static final int TRANSFORM_PIVOT_TARGET = 12;


        static {
            sMapToConstant.append(R.styleable.Transform_android_rotation, ROTATION);
            sMapToConstant.append(R.styleable.Transform_android_rotationX, ROTATION_X);
            sMapToConstant.append(R.styleable.Transform_android_rotationY, ROTATION_Y);
            sMapToConstant.append(R.styleable.Transform_android_scaleX, SCALE_X);
            sMapToConstant.append(R.styleable.Transform_android_scaleY, SCALE_Y);
            sMapToConstant.append(R.styleable.Transform_android_transformPivotX, TRANSFORM_PIVOT_X);
            sMapToConstant.append(R.styleable.Transform_android_transformPivotY, TRANSFORM_PIVOT_Y);
            sMapToConstant.append(R.styleable.Transform_android_translationX, TRANSLATION_X);
            sMapToConstant.append(R.styleable.Transform_android_translationY, TRANSLATION_Y);
            sMapToConstant.append(R.styleable.Transform_android_translationZ, TRANSLATION_Z);
            sMapToConstant.append(R.styleable.Transform_android_elevation, ELEVATION);
            sMapToConstant.append(R.styleable.Transform_transformPivotTarget,
                    TRANSFORM_PIVOT_TARGET);

        }

        void fillFromAttributeList(Context context, AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Transform);
            mApply = true;
            final int count = a.getIndexCount();
            for (int i = 0; i < count; i++) {
                int attr = a.getIndex(i);

                switch (sMapToConstant.get(attr)) {
                    case ROTATION:
                        rotation = a.getFloat(attr, rotation);
                        break;
                    case ROTATION_X:
                        rotationX = a.getFloat(attr, rotationX);
                        break;
                    case ROTATION_Y:
                        rotationY = a.getFloat(attr, rotationY);
                        break;
                    case SCALE_X:
                        scaleX = a.getFloat(attr, scaleX);
                        break;
                    case SCALE_Y:
                        scaleY = a.getFloat(attr, scaleY);
                        break;
                    case TRANSFORM_PIVOT_X:
                        transformPivotX = a.getDimension(attr, transformPivotX);
                        break;
                    case TRANSFORM_PIVOT_Y:
                        transformPivotY = a.getDimension(attr, transformPivotY);
                        break;
                    case TRANSFORM_PIVOT_TARGET:
                        transformPivotTarget = lookupID(a, attr, transformPivotTarget);
                        break;
                    case TRANSLATION_X:
                        translationX = a.getDimension(attr, translationX);
                        break;
                    case TRANSLATION_Y:
                        translationY = a.getDimension(attr, translationY);
                        break;
                    case TRANSLATION_Z:
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                            translationZ = a.getDimension(attr, translationZ);
                        }
                        break;
                    case ELEVATION:
                        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                            applyElevation = true;
                            elevation = a.getDimension(attr, elevation);
                        }
                        break;
                }
            }
            a.recycle();
        }
    }

    /**
     *
     */
    public static class PropertySet {
        public boolean mApply = false;
        public int visibility = View.VISIBLE;
        public int mVisibilityMode = VISIBILITY_MODE_NORMAL;
        public float alpha = 1;
        public float mProgress = Float.NaN;

        // @TODO: add description

        /**
         *
         * @param src
         */
        public void copyFrom(PropertySet src) {
            mApply = src.mApply;
            visibility = src.visibility;
            alpha = src.alpha;
            mProgress = src.mProgress;
            mVisibilityMode = src.mVisibilityMode;
        }

        void fillFromAttributeList(Context context, AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PropertySet);
            mApply = true;
            final int count = a.getIndexCount();
            for (int i = 0; i < count; i++) {
                int attr = a.getIndex(i);

                if (attr == R.styleable.PropertySet_android_alpha) {
                    alpha = a.getFloat(attr, alpha);
                } else if (attr == R.styleable.PropertySet_android_visibility) {
                    visibility = a.getInt(attr, visibility);
                    visibility = VISIBILITY_FLAGS[visibility];
                } else if (attr == R.styleable.PropertySet_visibilityMode) {
                    mVisibilityMode = a.getInt(attr, mVisibilityMode);
                } else if (attr == R.styleable.PropertySet_motionProgress) {
                    mProgress = a.getFloat(attr, mProgress);
                }
            }
            a.recycle();
        }
    }

    /**
     *
     */
    public static class Motion {
        public boolean mApply = false;
        public int mAnimateRelativeTo = Layout.UNSET;
        public int mAnimateCircleAngleTo = 0;
        public String mTransitionEasing = null;
        public int mPathMotionArc = Layout.UNSET;
        public int mDrawPath = 0;
        public float mMotionStagger = Float.NaN;
        public int mPolarRelativeTo = Layout.UNSET;
        public float mPathRotate = Float.NaN;
        public float mQuantizeMotionPhase = Float.NaN;
        public int mQuantizeMotionSteps = Layout.UNSET;
        public String mQuantizeInterpolatorString = null;
        public int mQuantizeInterpolatorType = INTERPOLATOR_UNDEFINED; // undefined
        public int mQuantizeInterpolatorID = -1;
        private static final int INTERPOLATOR_REFERENCE_ID = -2;
        private static final int SPLINE_STRING = -1;
        private static final int INTERPOLATOR_UNDEFINED = -3;

        // @TODO: add description

        /**
         *
         * @param src
         */
        public void copyFrom(Motion src) {
            mApply = src.mApply;
            mAnimateRelativeTo = src.mAnimateRelativeTo;
            mTransitionEasing = src.mTransitionEasing;
            mPathMotionArc = src.mPathMotionArc;
            mDrawPath = src.mDrawPath;
            mPathRotate = src.mPathRotate;
            mMotionStagger = src.mMotionStagger;
            mPolarRelativeTo = src.mPolarRelativeTo;
        }

        private static SparseIntArray sMapToConstant = new SparseIntArray();
        private static final int TRANSITION_PATH_ROTATE = 1;
        private static final int PATH_MOTION_ARC = 2;
        private static final int TRANSITION_EASING = 3;
        private static final int MOTION_DRAW_PATH = 4;
        private static final int ANIMATE_RELATIVE_TO = 5;
        private static final int ANIMATE_CIRCLE_ANGLE_TO = 6;
        private static final int MOTION_STAGGER = 7;
        private static final int QUANTIZE_MOTION_STEPS = 8;
        private static final int QUANTIZE_MOTION_PHASE = 9;
        private static final int QUANTIZE_MOTION_INTERPOLATOR = 10;


        static {
            sMapToConstant.append(R.styleable.Motion_motionPathRotate, TRANSITION_PATH_ROTATE);
            sMapToConstant.append(R.styleable.Motion_pathMotionArc, PATH_MOTION_ARC);
            sMapToConstant.append(R.styleable.Motion_transitionEasing, TRANSITION_EASING);
            sMapToConstant.append(R.styleable.Motion_drawPath, MOTION_DRAW_PATH);
            sMapToConstant.append(R.styleable.Motion_animateRelativeTo, ANIMATE_RELATIVE_TO);
            sMapToConstant.append(R.styleable.Motion_animateCircleAngleTo, ANIMATE_CIRCLE_ANGLE_TO);
            sMapToConstant.append(R.styleable.Motion_motionStagger, MOTION_STAGGER);
            sMapToConstant.append(R.styleable.Motion_quantizeMotionSteps, QUANTIZE_MOTION_STEPS);
            sMapToConstant.append(R.styleable.Motion_quantizeMotionPhase, QUANTIZE_MOTION_PHASE);
            sMapToConstant.append(R.styleable.Motion_quantizeMotionInterpolator,
                    QUANTIZE_MOTION_INTERPOLATOR);
        }

        void fillFromAttributeList(Context context, AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Motion);
            mApply = true;
            final int count = a.getIndexCount();
            for (int i = 0; i < count; i++) {
                int attr = a.getIndex(i);

                switch (sMapToConstant.get(attr)) {
                    case TRANSITION_PATH_ROTATE:
                        mPathRotate = a.getFloat(attr, mPathRotate);
                        break;
                    case PATH_MOTION_ARC:
                        mPathMotionArc = a.getInt(attr, mPathMotionArc);
                        break;
                    case TRANSITION_EASING:
                        TypedValue type = a.peekValue(attr);
                        if (type.type == TypedValue.TYPE_STRING) {
                            mTransitionEasing = a.getString(attr);
                        } else {
                            mTransitionEasing = Easing.NAMED_EASING[a.getInteger(attr, 0)];
                        }
                        break;
                    case MOTION_DRAW_PATH:
                        mDrawPath = a.getInt(attr, 0);
                        break;
                    case ANIMATE_RELATIVE_TO:
                        mAnimateRelativeTo = lookupID(a, attr, mAnimateRelativeTo);
                        break;
                    case ANIMATE_CIRCLE_ANGLE_TO:
                        mAnimateCircleAngleTo = a.getInteger(attr, mAnimateCircleAngleTo);
                        break;
                    case MOTION_STAGGER:
                        mMotionStagger = a.getFloat(attr, mMotionStagger);
                        break;
                    case QUANTIZE_MOTION_STEPS:
                        mQuantizeMotionSteps = a.getInteger(attr, mQuantizeMotionSteps);
                        break;
                    case QUANTIZE_MOTION_PHASE:
                        mQuantizeMotionPhase = a.getFloat(attr, mQuantizeMotionPhase);
                        break;
                    case QUANTIZE_MOTION_INTERPOLATOR:
                        type = a.peekValue(attr);

                        if (type.type == TypedValue.TYPE_REFERENCE) {
                            mQuantizeInterpolatorID = a.getResourceId(attr, -1);
                            if (mQuantizeInterpolatorID != -1) {
                                mQuantizeInterpolatorType = INTERPOLATOR_REFERENCE_ID;
                            }
                        } else if (type.type == TypedValue.TYPE_STRING) {
                            mQuantizeInterpolatorString = a.getString(attr);
                            if (mQuantizeInterpolatorString.indexOf("/") > 0) {
                                mQuantizeInterpolatorID = a.getResourceId(attr, -1);
                                mQuantizeInterpolatorType = INTERPOLATOR_REFERENCE_ID;
                            } else {
                                mQuantizeInterpolatorType = SPLINE_STRING;
                            }
                        } else {
                            mQuantizeInterpolatorType = a.getInteger(attr, mQuantizeInterpolatorID);
                        }

                        break;
                }
            }
            a.recycle();
        }
    }

    /**
     *
     */
    public static class Constraint {
        int mViewId;
        String mTargetString;
        public final PropertySet propertySet = new PropertySet();
        public final Motion motion = new Motion();
        public final Layout layout = new Layout();
        public final Transform transform = new Transform();
        public HashMap<String, ConstraintAttribute> mCustomConstraints = new HashMap<>();
        Delta mDelta;

        static class Delta {
            private static final int INITIAL_BOOLEAN = 4;
            private static final int INITIAL_INT = 10;
            private static final int INITIAL_FLOAT = 10;
            private static final int INITIAL_STRING = 5;
            int[] mTypeInt = new int[INITIAL_INT];
            int[] mValueInt = new int[INITIAL_INT];
            int mCountInt = 0;

            void add(int type, int value) {
                if (mCountInt >= mTypeInt.length) {
                    mTypeInt = Arrays.copyOf(mTypeInt, mTypeInt.length * 2);
                    mValueInt = Arrays.copyOf(mValueInt, mValueInt.length * 2);
                }
                mTypeInt[mCountInt] = type;
                mValueInt[mCountInt++] = value;
            }

            int[] mTypeFloat = new int[INITIAL_FLOAT];
            float[] mValueFloat = new float[INITIAL_FLOAT];
            int mCountFloat = 0;

            void add(int type, float value) {
                if (mCountFloat >= mTypeFloat.length) {
                    mTypeFloat = Arrays.copyOf(mTypeFloat, mTypeFloat.length * 2);
                    mValueFloat = Arrays.copyOf(mValueFloat, mValueFloat.length * 2);
                }
                mTypeFloat[mCountFloat] = type;
                mValueFloat[mCountFloat++] = value;
            }

            int[] mTypeString = new int[INITIAL_STRING];
            String[] mValueString = new String[INITIAL_STRING];
            int mCountString = 0;

            void add(int type, String value) {
                if (mCountString >= mTypeString.length) {
                    mTypeString = Arrays.copyOf(mTypeString, mTypeString.length * 2);
                    mValueString = Arrays.copyOf(mValueString, mValueString.length * 2);
                }
                mTypeString[mCountString] = type;
                mValueString[mCountString++] = value;
            }

            int[] mTypeBoolean = new int[INITIAL_BOOLEAN];
            boolean[] mValueBoolean = new boolean[INITIAL_BOOLEAN];
            int mCountBoolean = 0;

            void add(int type, boolean value) {
                if (mCountBoolean >= mTypeBoolean.length) {
                    mTypeBoolean = Arrays.copyOf(mTypeBoolean, mTypeBoolean.length * 2);
                    mValueBoolean = Arrays.copyOf(mValueBoolean, mValueBoolean.length * 2);
                }
                mTypeBoolean[mCountBoolean] = type;
                mValueBoolean[mCountBoolean++] = value;
            }

            void applyDelta(Constraint c) {
                for (int i = 0; i < mCountInt; i++) {
                    setDeltaValue(c, mTypeInt[i], mValueInt[i]);
                }
                for (int i = 0; i < mCountFloat; i++) {
                    setDeltaValue(c, mTypeFloat[i], mValueFloat[i]);
                }
                for (int i = 0; i < mCountString; i++) {
                    setDeltaValue(c, mTypeString[i], mValueString[i]);
                }
                for (int i = 0; i < mCountBoolean; i++) {
                    setDeltaValue(c, mTypeBoolean[i], mValueBoolean[i]);
                }
            }

            @SuppressLint("LogConditional")
            void printDelta(String tag) {
                Log.v(tag, "int");

                for (int i = 0; i < mCountInt; i++) {
                    Log.v(tag, mTypeInt[i] + " = " + mValueInt[i]);
                }
                Log.v(tag, "float");

                for (int i = 0; i < mCountFloat; i++) {
                    Log.v(tag, mTypeFloat[i] + " = " + mValueFloat[i]);
                }
                Log.v(tag, "strings");

                for (int i = 0; i < mCountString; i++) {
                    Log.v(tag, mTypeString[i] + " = " + mValueString[i]);
                }
                Log.v(tag, "boolean");
                for (int i = 0; i < mCountBoolean; i++) {
                    Log.v(tag, mTypeBoolean[i] + " = " + mValueBoolean[i]);
                }
            }
        }

        /**
         * Apply a delta to a constraint
         * @param c
         */
        public void applyDelta(Constraint c) {
            if (mDelta != null) {
                mDelta.applyDelta(c);
            }
        }

        /**
         * Apply a delta file
         * @param tag
         */
        public void printDelta(String tag) {
            if (mDelta != null) {
                mDelta.printDelta(tag);
            } else {
                Log.v(tag, "DELTA IS NULL");
            }
        }

        private ConstraintAttribute get(String attributeName, AttributeType attributeType) {
            ConstraintAttribute ret;
            if (mCustomConstraints.containsKey(attributeName)) {
                ret = mCustomConstraints.get(attributeName);
                if (ret.getType() != attributeType) {
                    throw new IllegalArgumentException(
                            "ConstraintAttribute is already a " + ret.getType().name());
                }
            } else {
                ret = new ConstraintAttribute(attributeName, attributeType);
                mCustomConstraints.put(attributeName, ret);
            }
            return ret;
        }

        private void setStringValue(String attributeName, String value) {
            get(attributeName, AttributeType.STRING_TYPE).setStringValue(value);
        }

        private void setFloatValue(String attributeName, float value) {
            get(attributeName, AttributeType.FLOAT_TYPE).setFloatValue(value);
        }

        private void setIntValue(String attributeName, int value) {
            get(attributeName, AttributeType.INT_TYPE).setIntValue(value);
        }

        private void setColorValue(String attributeName, int value) {
            get(attributeName, AttributeType.COLOR_TYPE).setColorValue(value);
        }

        /**
         * Return a copy of the Constraint
         * @return
         */
        @Override
        public Constraint clone() {
            Constraint clone = new Constraint();
            clone.layout.copyFrom(layout);
            clone.motion.copyFrom(motion);
            clone.propertySet.copyFrom(propertySet);
            clone.transform.copyFrom(transform);
            clone.mViewId = mViewId;
            clone.mDelta = mDelta;
            return clone;
        }

        private void fillFromConstraints(ConstraintHelper helper,
                                         int viewId,
                                         Constraints.LayoutParams param) {
            fillFromConstraints(viewId, param);
            if (helper instanceof Barrier) {
                layout.mHelperType = BARRIER_TYPE;
                Barrier barrier = (Barrier) helper;
                layout.mBarrierDirection = barrier.getType();
                layout.mReferenceIds = barrier.getReferencedIds();
                layout.mBarrierMargin = barrier.getMargin();
            }
        }

        private void fillFromConstraints(int viewId, Constraints.LayoutParams param) {
            fillFrom(viewId, param);
            propertySet.alpha = param.alpha;
            transform.rotation = param.rotation;
            transform.rotationX = param.rotationX;
            transform.rotationY = param.rotationY;
            transform.scaleX = param.scaleX;
            transform.scaleY = param.scaleY;
            transform.transformPivotX = param.transformPivotX;
            transform.transformPivotY = param.transformPivotY;
            transform.translationX = param.translationX;
            transform.translationY = param.translationY;
            transform.translationZ = param.translationZ;
            transform.elevation = param.elevation;
            transform.applyElevation = param.applyElevation;
        }

        private void fillFrom(int viewId, ConstraintLayout.LayoutParams param) {
            mViewId = viewId;
            layout.leftToLeft = param.leftToLeft;
            layout.leftToRight = param.leftToRight;
            layout.rightToLeft = param.rightToLeft;
            layout.rightToRight = param.rightToRight;
            layout.topToTop = param.topToTop;
            layout.topToBottom = param.topToBottom;
            layout.bottomToTop = param.bottomToTop;
            layout.bottomToBottom = param.bottomToBottom;
            layout.baselineToBaseline = param.baselineToBaseline;
            layout.baselineToTop = param.baselineToTop;
            layout.baselineToBottom = param.baselineToBottom;
            layout.startToEnd = param.startToEnd;
            layout.startToStart = param.startToStart;
            layout.endToStart = param.endToStart;
            layout.endToEnd = param.endToEnd;

            layout.horizontalBias = param.horizontalBias;
            layout.verticalBias = param.verticalBias;
            layout.dimensionRatio = param.dimensionRatio;

            layout.circleConstraint = param.circleConstraint;
            layout.circleRadius = param.circleRadius;
            layout.circleAngle = param.circleAngle;

            layout.editorAbsoluteX = param.editorAbsoluteX;
            layout.editorAbsoluteY = param.editorAbsoluteY;
            layout.orientation = param.orientation;
            layout.guidePercent = param.guidePercent;
            layout.guideBegin = param.guideBegin;
            layout.guideEnd = param.guideEnd;
            layout.mWidth = param.width;
            layout.mHeight = param.height;
            layout.leftMargin = param.leftMargin;
            layout.rightMargin = param.rightMargin;
            layout.topMargin = param.topMargin;
            layout.bottomMargin = param.bottomMargin;
            layout.baselineMargin = param.baselineMargin;
            layout.verticalWeight = param.verticalWeight;
            layout.horizontalWeight = param.horizontalWeight;
            layout.verticalChainStyle = param.verticalChainStyle;
            layout.horizontalChainStyle = param.horizontalChainStyle;
            layout.constrainedWidth = param.constrainedWidth;
            layout.constrainedHeight = param.constrainedHeight;
            layout.widthDefault = param.matchConstraintDefaultWidth;
            layout.heightDefault = param.matchConstraintDefaultHeight;
            layout.widthMax = param.matchConstraintMaxWidth;
            layout.heightMax = param.matchConstraintMaxHeight;
            layout.widthMin = param.matchConstraintMinWidth;
            layout.heightMin = param.matchConstraintMinHeight;
            layout.widthPercent = param.matchConstraintPercentWidth;
            layout.heightPercent = param.matchConstraintPercentHeight;
            layout.mConstraintTag = param.constraintTag;
            layout.goneTopMargin = param.goneTopMargin;
            layout.goneBottomMargin = param.goneBottomMargin;
            layout.goneLeftMargin = param.goneLeftMargin;
            layout.goneRightMargin = param.goneRightMargin;
            layout.goneStartMargin = param.goneStartMargin;
            layout.goneEndMargin = param.goneEndMargin;
            layout.goneBaselineMargin = param.goneBaselineMargin;
            layout.mWrapBehavior = param.wrapBehaviorInParent;
            layout.endMargin = param.getMarginEnd();
            layout.startMargin = param.getMarginStart();
        }

        /**
         * apply ConstraintSet to the layout params
         * @param param
         */
        public void applyTo(ConstraintLayout.LayoutParams param) {
            param.leftToLeft = layout.leftToLeft;
            param.leftToRight = layout.leftToRight;
            param.rightToLeft = layout.rightToLeft;
            param.rightToRight = layout.rightToRight;

            param.topToTop = layout.topToTop;
            param.topToBottom = layout.topToBottom;
            param.bottomToTop = layout.bottomToTop;
            param.bottomToBottom = layout.bottomToBottom;

            param.baselineToBaseline = layout.baselineToBaseline;
            param.baselineToTop = layout.baselineToTop;
            param.baselineToBottom = layout.baselineToBottom;

            param.startToEnd = layout.startToEnd;
            param.startToStart = layout.startToStart;
            param.endToStart = layout.endToStart;
            param.endToEnd = layout.endToEnd;

            param.leftMargin = layout.leftMargin;
            param.rightMargin = layout.rightMargin;
            param.topMargin = layout.topMargin;
            param.bottomMargin = layout.bottomMargin;
            param.goneStartMargin = layout.goneStartMargin;
            param.goneEndMargin = layout.goneEndMargin;
            param.goneTopMargin = layout.goneTopMargin;
            param.goneBottomMargin = layout.goneBottomMargin;

            param.horizontalBias = layout.horizontalBias;
            param.verticalBias = layout.verticalBias;

            param.circleConstraint = layout.circleConstraint;
            param.circleRadius = layout.circleRadius;
            param.circleAngle = layout.circleAngle;

            param.dimensionRatio = layout.dimensionRatio;
            param.editorAbsoluteX = layout.editorAbsoluteX;
            param.editorAbsoluteY = layout.editorAbsoluteY;
            param.verticalWeight = layout.verticalWeight;
            param.horizontalWeight = layout.horizontalWeight;
            param.verticalChainStyle = layout.verticalChainStyle;
            param.horizontalChainStyle = layout.horizontalChainStyle;
            param.constrainedWidth = layout.constrainedWidth;
            param.constrainedHeight = layout.constrainedHeight;
            param.matchConstraintDefaultWidth = layout.widthDefault;
            param.matchConstraintDefaultHeight = layout.heightDefault;
            param.matchConstraintMaxWidth = layout.widthMax;
            param.matchConstraintMaxHeight = layout.heightMax;
            param.matchConstraintMinWidth = layout.widthMin;
            param.matchConstraintMinHeight = layout.heightMin;
            param.matchConstraintPercentWidth = layout.widthPercent;
            param.matchConstraintPercentHeight = layout.heightPercent;
            param.orientation = layout.orientation;
            param.guidePercent = layout.guidePercent;
            param.guideBegin = layout.guideBegin;
            param.guideEnd = layout.guideEnd;
            param.width = layout.mWidth;
            param.height = layout.mHeight;
            if (layout.mConstraintTag != null) {
                param.constraintTag = layout.mConstraintTag;
            }
            param.wrapBehaviorInParent = layout.mWrapBehavior;

            param.setMarginStart(layout.startMargin);
            param.setMarginEnd(layout.endMargin);

            param.validate();
        }

    }

    /**
     * Copy the constraints from a layout.
     *
     * @param context            the context for the layout inflation
     * @param constraintLayoutId the id of the layout file
     */
    public void clone(Context context, int constraintLayoutId) {
        clone((ConstraintLayout) LayoutInflater.from(context).inflate(constraintLayoutId, null));
    }

    /**
     * Copy the constraints from a layout.
     *
     * @param set constraint set to copy
     */
    public void clone(ConstraintSet set) {
        mConstraints.clear();
        for (Integer key : set.mConstraints.keySet()) {
            Constraint constraint = set.mConstraints.get(key);
            if (constraint == null) {
                continue;
            }
            mConstraints.put(key, constraint.clone());
        }
    }

    /**
     * Copy the layout parameters of a ConstraintLayout.
     *
     * @param constraintLayout The ConstraintLayout to be copied
     */
    public void clone(ConstraintLayout constraintLayout) {
        int count = constraintLayout.getChildCount();
        mConstraints.clear();
        for (int i = 0; i < count; i++) {
            View view = constraintLayout.getChildAt(i);
            ConstraintLayout.LayoutParams param =
                    (ConstraintLayout.LayoutParams) view.getLayoutParams();

            int id = view.getId();
            if (mForceId && id == -1) {
                throw new RuntimeException("All children of ConstraintLayout must "
                        + "have ids to use ConstraintSet");
            }
            if (!mConstraints.containsKey(id)) {
                mConstraints.put(id, new Constraint());
            }
            Constraint constraint = mConstraints.get(id);
            if (constraint == null) {
                continue;
            }
            constraint.mCustomConstraints =
                    ConstraintAttribute.extractAttributes(mSavedAttributes, view);
            constraint.fillFrom(id, param);
            constraint.propertySet.visibility = view.getVisibility();
            constraint.propertySet.alpha = view.getAlpha();
            constraint.transform.rotation = view.getRotation();
            constraint.transform.rotationX = view.getRotationX();
            constraint.transform.rotationY = view.getRotationY();
            constraint.transform.scaleX = view.getScaleX();
            constraint.transform.scaleY = view.getScaleY();

            float pivotX = view.getPivotX(); // we assume it is not set if set to 0.0
            float pivotY = view.getPivotY(); // we assume it is not set if set to 0.0

            if (pivotX != 0.0 || pivotY != 0.0) {
                constraint.transform.transformPivotX = pivotX;
                constraint.transform.transformPivotY = pivotY;
            }

            constraint.transform.translationX = view.getTranslationX();
            constraint.transform.translationY = view.getTranslationY();
            if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                constraint.transform.translationZ = view.getTranslationZ();
                if (constraint.transform.applyElevation) {
                    constraint.transform.elevation = view.getElevation();
                }
            }
            if (view instanceof Barrier) {
                Barrier barrier = ((Barrier) view);
                constraint.layout.mBarrierAllowsGoneWidgets = barrier.getAllowsGoneWidget();
                constraint.layout.mReferenceIds = barrier.getReferencedIds();
                constraint.layout.mBarrierDirection = barrier.getType();
                constraint.layout.mBarrierMargin = barrier.getMargin();
            }
        }
    }

    /**
     * Copy the layout parameters of a ConstraintLayout.
     *
     * @param constraints The ConstraintLayout to be copied
     */
    public void clone(Constraints constraints) {
        int count = constraints.getChildCount();
        mConstraints.clear();
        for (int i = 0; i < count; i++) {
            View view = constraints.getChildAt(i);
            Constraints.LayoutParams param = (Constraints.LayoutParams) view.getLayoutParams();

            int id = view.getId();
            if (mForceId && id == -1) {
                throw new RuntimeException("All children of ConstraintLayout "
                        + "must have ids to use ConstraintSet");
            }
            if (!mConstraints.containsKey(id)) {
                mConstraints.put(id, new Constraint());
            }
            Constraint constraint = mConstraints.get(id);
            if (constraint == null) {
                continue;
            }
            if (view instanceof ConstraintHelper) {
                ConstraintHelper helper = (ConstraintHelper) view;
                constraint.fillFromConstraints(helper, id, param);
            }
            constraint.fillFromConstraints(id, param);
        }
    }

    /**
     * Apply the constraints to a ConstraintLayout.
     *
     * @param constraintLayout to be modified
     */
    public void applyTo(ConstraintLayout constraintLayout) {
        applyToInternal(constraintLayout, true);
        constraintLayout.setConstraintSet(null);
        constraintLayout.requestLayout();
    }


    /**
     * Apply the constraints to a ConstraintLayout.
     *
     * @param constraintLayout to be modified
     */
    public void applyToWithoutCustom(ConstraintLayout constraintLayout) {
        applyToInternal(constraintLayout, false);
        constraintLayout.setConstraintSet(null);
    }

    /**
     * Apply custom attributes alone
     *
     * @param constraintLayout
     */
    public void applyCustomAttributes(ConstraintLayout constraintLayout) {
        int count = constraintLayout.getChildCount();
        for (int i = 0; i < count; i++) {
            View view = constraintLayout.getChildAt(i);
            int id = view.getId();
            if (!mConstraints.containsKey(id)) {
                Log.w(TAG, "id unknown " + Debug.getName(view));
                continue;
            }
            if (mForceId && id == -1) {
                throw new RuntimeException("All children of ConstraintLayout "
                        + "must have ids to use ConstraintSet");
            }

            if (mConstraints.containsKey(id)) {
                Constraint constraint = mConstraints.get(id);
                if (constraint == null) {
                    continue;
                }
                ConstraintAttribute.setAttributes(view, constraint.mCustomConstraints);
            }
        }
    }

    /**
     * Apply Layout to Helper widget
     *
     * @param helper
     * @param child
     * @param layoutParams
     * @param mapIdToWidget
     */
    public void applyToHelper(ConstraintHelper helper, ConstraintWidget child,
                              LayoutParams layoutParams,
                              SparseArray<ConstraintWidget> mapIdToWidget) {
        int id = helper.getId();
        if (mConstraints.containsKey(id)) {
            Constraint constraint = mConstraints.get(id);
            if (constraint != null && child instanceof HelperWidget) {
                HelperWidget helperWidget = (HelperWidget) child;
                helper.loadParameters(constraint, helperWidget, layoutParams, mapIdToWidget);
            }
        }
    }

    /**
     * Fill in a ConstraintLayout LayoutParam based on the id.
     *
     * @param id           Id of the view
     * @param layoutParams LayoutParams to be filled
     */
    public void applyToLayoutParams(int id, ConstraintLayout.LayoutParams layoutParams) {
        if (mConstraints.containsKey(id)) {
            Constraint constraint = mConstraints.get(id);
            if (constraint != null) {
                constraint.applyTo(layoutParams);
            }
        }
    }

    /**
     * Used to set constraints when used by constraint layout
     */
    void applyToInternal(ConstraintLayout constraintLayout, boolean applyPostLayout) {
        int count = constraintLayout.getChildCount();
        HashSet<Integer> used = new HashSet<Integer>(mConstraints.keySet());
        for (int i = 0; i < count; i++) {
            View view = constraintLayout.getChildAt(i);
            int id = view.getId();
            if (!mConstraints.containsKey(id)) {
                Log.w(TAG, "id unknown " + Debug.getName(view));
                continue;
            }

            if (mForceId && id == -1) {
                throw new RuntimeException("All children of ConstraintLayout "
                        + "must have ids to use ConstraintSet");
            }
            if (id == -1) {
                continue;
            }

            if (mConstraints.containsKey(id)) {
                used.remove(id);
                Constraint constraint = mConstraints.get(id);
                if (constraint == null) {
                    continue;
                }
                if (view instanceof Barrier) {
                    constraint.layout.mHelperType = BARRIER_TYPE;
                    Barrier barrier = (Barrier) view;
                    barrier.setId(id);
                    barrier.setType(constraint.layout.mBarrierDirection);
                    barrier.setMargin(constraint.layout.mBarrierMargin);

                    barrier.setAllowsGoneWidget(constraint.layout.mBarrierAllowsGoneWidgets);
                    if (constraint.layout.mReferenceIds != null) {
                        barrier.setReferencedIds(constraint.layout.mReferenceIds);
                    } else if (constraint.layout.mReferenceIdString != null) {
                        constraint.layout.mReferenceIds = convertReferenceString(barrier,
                                constraint.layout.mReferenceIdString);
                        barrier.setReferencedIds(constraint.layout.mReferenceIds);
                    }
                }
                ConstraintLayout.LayoutParams param = (ConstraintLayout.LayoutParams) view
                        .getLayoutParams();
                param.validate();
                constraint.applyTo(param);

                if (applyPostLayout) {
                    ConstraintAttribute.setAttributes(view, constraint.mCustomConstraints);
                }
                view.setLayoutParams(param);
                if (constraint.propertySet.mVisibilityMode == VISIBILITY_MODE_NORMAL) {
                    view.setVisibility(constraint.propertySet.visibility);
                }
                view.setAlpha(constraint.propertySet.alpha);
                view.setRotation(constraint.transform.rotation);
                view.setRotationX(constraint.transform.rotationX);
                view.setRotationY(constraint.transform.rotationY);
                view.setScaleX(constraint.transform.scaleX);
                view.setScaleY(constraint.transform.scaleY);
                if (constraint.transform.transformPivotTarget != UNSET) {
                    View layout = (View) view.getParent();
                    View center = layout.findViewById(
                            constraint.transform.transformPivotTarget);
                    if (center != null) {
                        float cy = (center.getTop() + center.getBottom()) / 2.0f;
                        float cx = (center.getLeft() + center.getRight()) / 2.0f;
                        if (view.getRight() - view.getLeft() > 0
                                && view.getBottom() - view.getTop() > 0) {
                            float px = (cx - view.getLeft());
                            float py = (cy - view.getTop());
                            view.setPivotX(px);
                            view.setPivotY(py);
                        }
                    }
                } else {
                    if (!Float.isNaN(constraint.transform.transformPivotX)) {
                        view.setPivotX(constraint.transform.transformPivotX);
                    }
                    if (!Float.isNaN(constraint.transform.transformPivotY)) {
                        view.setPivotY(constraint.transform.transformPivotY);
                    }
                }
                view.setTranslationX(constraint.transform.translationX);
                view.setTranslationY(constraint.transform.translationY);
                if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                    view.setTranslationZ(constraint.transform.translationZ);
                    if (constraint.transform.applyElevation) {
                        view.setElevation(constraint.transform.elevation);
                    }
                }
            } else {
                Log.v(TAG, "WARNING NO CONSTRAINTS for view " + id);
            }
        }
        for (Integer id : used) {
            Constraint constraint = mConstraints.get(id);
            if (constraint == null) {
                continue;
            }
            if (constraint.layout.mHelperType == BARRIER_TYPE) {
                Barrier barrier = new Barrier(constraintLayout.getContext());
                barrier.setId(id);
                if (constraint.layout.mReferenceIds != null) {
                    barrier.setReferencedIds(constraint.layout.mReferenceIds);
                } else if (constraint.layout.mReferenceIdString != null) {
                    constraint.layout.mReferenceIds = convertReferenceString(barrier,
                            constraint.layout.mReferenceIdString);
                    barrier.setReferencedIds(constraint.layout.mReferenceIds);
                }
                barrier.setType(constraint.layout.mBarrierDirection);
                barrier.setMargin(constraint.layout.mBarrierMargin);
                LayoutParams param = constraintLayout
                        .generateDefaultLayoutParams();
                barrier.validateParams();
                constraint.applyTo(param);
                constraintLayout.addView(barrier, param);
            }
            if (constraint.layout.mIsGuideline) {
                Guideline g = new Guideline(constraintLayout.getContext());
                g.setId(id);
                ConstraintLayout.LayoutParams param =
                        constraintLayout.generateDefaultLayoutParams();
                constraint.applyTo(param);
                constraintLayout.addView(g, param);
            }
        }
        for (int i = 0; i < count; i++) {
            View view = constraintLayout.getChildAt(i);
            if (view instanceof ConstraintHelper) {
                ConstraintHelper constraintHelper = (ConstraintHelper) view;
                constraintHelper.applyLayoutFeaturesInConstraintSet(constraintLayout);
            }
        }
    }

    /**
     * Center widget between the other two widgets.
     * (for sides see: {@link #TOP}, {@link #BOTTOM}, {@link #START},
     * {@link #END}, {@link #LEFT}, {@link #RIGHT})
     * Note, sides must be all vertical or horizontal sides.
     *
     * @param centerID     ID of the widget to be centered
     * @param firstID      ID of the first widget to connect the left or top of the widget to
     * @param firstSide    the side of the widget to connect to
     * @param firstMargin  the connection margin
     * @param secondId     the ID of the second widget to connect to right or top of the widget to
     * @param secondSide   the side of the widget to connect to
     * @param secondMargin the connection margin
     * @param bias         the ratio between two connections
     */
    public void center(int centerID,
                       int firstID, int firstSide, int firstMargin,
                       int secondId, int secondSide, int secondMargin,
                       float bias) {
        // Error checking

        if (firstMargin < 0) {
            throw new IllegalArgumentException("margin must be > 0");
        }
        if (secondMargin < 0) {
            throw new IllegalArgumentException("margin must be > 0");
        }
        if (bias <= 0 || bias > 1) {
            throw new IllegalArgumentException("bias must be between 0 and 1 inclusive");
        }

        if (firstSide == LEFT || firstSide == RIGHT) {
            connect(centerID, LEFT, firstID, firstSide, firstMargin);
            connect(centerID, RIGHT, secondId, secondSide, secondMargin);
            Constraint constraint = mConstraints.get(centerID);
            if (constraint != null) {
                constraint.layout.horizontalBias = bias;
            }
        } else if (firstSide == START || firstSide == END) {
            connect(centerID, START, firstID, firstSide, firstMargin);
            connect(centerID, END, secondId, secondSide, secondMargin);
            Constraint constraint = mConstraints.get(centerID);
            if (constraint != null) {
                constraint.layout.horizontalBias = bias;
            }
        } else {
            connect(centerID, TOP, firstID, firstSide, firstMargin);
            connect(centerID, BOTTOM, secondId, secondSide, secondMargin);
            Constraint constraint = mConstraints.get(centerID);
            if (constraint != null) {
                constraint.layout.verticalBias = bias;
            }
        }
    }

    /**
     * Centers the widget horizontally to the left and right side on another widgets sides.
     * (for sides see: {@link #START}, {@link #END}, {@link #LEFT}, {@link #RIGHT})
     *
     * @param centerID    ID of widget to be centered
     * @param leftId      The Id of the widget on the left side
     * @param leftSide    The side of the leftId widget to connect to
     * @param leftMargin  The margin on the left side
     * @param rightId     The Id of the widget on the right side
     * @param rightSide   The side  of the rightId widget to connect to
     * @param rightMargin The margin on the right side
     * @param bias        The ratio of the space on the left vs.
     * right sides 0.5 is centered (default)
     */
    public void centerHorizontally(int centerID,
                                   int leftId,
                                   int leftSide,
                                   int leftMargin,
                                   int rightId,
                                   int rightSide,
                                   int rightMargin,
                                   float bias) {
        connect(centerID, LEFT, leftId, leftSide, leftMargin);
        connect(centerID, RIGHT, rightId, rightSide, rightMargin);
        Constraint constraint = mConstraints.get(centerID);
        if (constraint != null) {
            constraint.layout.horizontalBias = bias;
        }
    }

    /**
     * Centers the widgets horizontally to the left and right side on another widgets sides.
     * (for sides see: {@link #START}, {@link #END},
     * {@link #LEFT}, {@link #RIGHT})
     *
     * @param centerID    ID of widget to be centered
     * @param startId     The Id of the widget on the start side (left in non rtl languages)
     * @param startSide   The side of the startId widget to connect to
     * @param startMargin The margin on the start side
     * @param endId       The Id of the widget on the start side (left in non rtl languages)
     * @param endSide     The side of the endId widget to connect to
     * @param endMargin   The margin on the end side
     * @param bias        The ratio of the space on the start vs end side 0.5 is centered (default)
     */
    public void centerHorizontallyRtl(int centerID, int startId, int startSide, int startMargin,
                                      int endId, int endSide, int endMargin, float bias) {
        connect(centerID, START, startId, startSide, startMargin);
        connect(centerID, END, endId, endSide, endMargin);
        Constraint constraint = mConstraints.get(centerID);
        if (constraint != null) {
            constraint.layout.horizontalBias = bias;
        }
    }

    /**
     * Centers the widgets vertically to the top and bottom side on another widgets sides.
     * (for sides see: {@link #TOP}, {@link #BOTTOM})
     *
     * @param centerID     ID of widget to be centered
     * @param topId        The Id of the widget on the top side
     * @param topSide      The side of the leftId widget to connect to
     * @param topMargin    The margin on the top side
     * @param bottomId     The Id of the widget on the bottom side
     * @param bottomSide   The side of the bottomId widget to connect to
     * @param bottomMargin The margin on the bottom side
     * @param bias         The ratio of the space on the top vs.
     * bottom sides 0.5 is centered (default)
     */
    public void centerVertically(int centerID, int topId, int topSide, int topMargin, int bottomId,
                                 int bottomSide, int bottomMargin, float bias) {
        connect(centerID, TOP, topId, topSide, topMargin);
        connect(centerID, BOTTOM, bottomId, bottomSide, bottomMargin);
        Constraint constraint = mConstraints.get(centerID);
        if (constraint != null) {
            constraint.layout.verticalBias = bias;
        }
    }

    /**
     * Spaces a set of widgets vertically between the view topId and bottomId.
     * Widgets can be spaced with weights.
     * This operation sets all the related margins to 0.
     * <p>
     * (for sides see: {@link #TOP}, {@link #BOTTOM})
     *
     * @param topId      The id of the widget to connect to or PARENT_ID
     * @param topSide    the side of the start to connect to
     * @param bottomId   The id of the widget to connect to or PARENT_ID
     * @param bottomSide the side of the right to connect to
     * @param chainIds   widgets to use as a chain
     * @param weights    can be null
     * @param style      set the style of the chain
     */
    public void createVerticalChain(int topId,
                                    int topSide,
                                    int bottomId,
                                    int bottomSide,
                                    int[] chainIds,
                                    float[] weights,
                                    int style) {
        if (chainIds.length < 2) {
            throw new IllegalArgumentException("must have 2 or more widgets in a chain");
        }
        if (weights != null && weights.length != chainIds.length) {
            throw new IllegalArgumentException("must have 2 or more widgets in a chain");
        }
        if (weights != null) {
            get(chainIds[0]).layout.verticalWeight = weights[0];
        }
        get(chainIds[0]).layout.verticalChainStyle = style;

        connect(chainIds[0], TOP, topId, topSide, 0);
        for (int i = 1; i < chainIds.length; i++) {
            connect(chainIds[i], TOP, chainIds[i - 1], BOTTOM, 0);
            connect(chainIds[i - 1], BOTTOM, chainIds[i], TOP, 0);
            if (weights != null) {
                get(chainIds[i]).layout.verticalWeight = weights[i];
            }
        }
        connect(chainIds[chainIds.length - 1], BOTTOM, bottomId, bottomSide, 0);
    }

    /**
     * Spaces a set of widgets horizontally between the view startID and endId.
     * Widgets can be spaced with weights.
     * This operation sets all the related margins to 0.
     * <p>
     * (for sides see: {@link #START}, {@link #END},
     * {@link #LEFT}, {@link #RIGHT})
     *
     * @param leftId    The id of the widget to connect to or PARENT_ID
     * @param leftSide  the side of the start to connect to
     * @param rightId   The id of the widget to connect to or PARENT_ID
     * @param rightSide the side of the right to connect to
     * @param chainIds  The widgets in the chain
     * @param weights   The weight to assign to each element in the chain or null
     * @param style     The type of chain
     */
    public void createHorizontalChain(int leftId,
                                      int leftSide,
                                      int rightId,
                                      int rightSide,
                                      int[] chainIds,
                                      float[] weights,
                                      int style) {
        createHorizontalChain(leftId, leftSide, rightId, rightSide,
                chainIds, weights, style, LEFT, RIGHT);
    }

    /**
     * Spaces a set of widgets horizontal between the view startID and endId.
     * Widgets can be spaced with weights.
     * (for sides see: {@link #START}, {@link #END},
     * {@link #LEFT}, {@link #RIGHT})
     *
     * @param startId   The id of the widget to connect to or PARENT_ID
     * @param startSide the side of the start to connect to
     * @param endId     The id of the widget to connect to or PARENT_ID
     * @param endSide   the side of the end to connect to
     * @param chainIds  The widgets in the chain
     * @param weights   The weight to assign to each element in the chain or null
     * @param style     The type of chain
     */
    public void createHorizontalChainRtl(int startId,
                                         int startSide,
                                         int endId,
                                         int endSide,
                                         int[] chainIds,
                                         float[] weights,
                                         int style) {
        createHorizontalChain(startId, startSide, endId, endSide,
                chainIds, weights, style, START, END);
    }

    private void createHorizontalChain(int leftId,
                                       int leftSide,
                                       int rightId,
                                       int rightSide,
                                       int[] chainIds,
                                       float[] weights,
                                       int style, int left, int right) {

        if (chainIds.length < 2) {
            throw new IllegalArgumentException("must have 2 or more widgets in a chain");
        }
        if (weights != null && weights.length != chainIds.length) {
            throw new IllegalArgumentException("must have 2 or more widgets in a chain");
        }
        if (weights != null) {
            get(chainIds[0]).layout.horizontalWeight = weights[0];
        }
        get(chainIds[0]).layout.horizontalChainStyle = style;
        connect(chainIds[0], left, leftId, leftSide, UNSET);
        for (int i = 1; i < chainIds.length; i++) {
            connect(chainIds[i], left, chainIds[i - 1], right, UNSET);
            connect(chainIds[i - 1], right, chainIds[i], left, UNSET);
            if (weights != null) {
                get(chainIds[i]).layout.horizontalWeight = weights[i];
            }
        }

        connect(chainIds[chainIds.length - 1], right, rightId, rightSide,
                UNSET);

    }

    /**
     * Create a constraint between two widgets.
     * (for sides see: {@link #TOP}, {@link #BOTTOM}, {@link #START}, {@link #END},
     * {@link #LEFT}, {@link #RIGHT}, {@link #BASELINE})
     *
     * @param startID   the ID of the widget to be constrained
     * @param startSide the side of the widget to constrain
     * @param endID     the id of the widget to constrain to
     * @param endSide   the side of widget to constrain to
     * @param margin    the margin to constrain (margin must be positive)
     */
    public void connect(int startID, int startSide, int endID, int endSide, int margin) {
        if (!mConstraints.containsKey(startID)) {
            mConstraints.put(startID, new Constraint());
        }
        Constraint constraint = mConstraints.get(startID);
        if (constraint == null) {
            return;
        }
        switch (startSide) {
            case LEFT:
                if (endSide == LEFT) {
                    constraint.layout.leftToLeft = endID;
                    constraint.layout.leftToRight = Layout.UNSET;
                } else if (endSide == RIGHT) {
                    constraint.layout.leftToRight = endID;
                    constraint.layout.leftToLeft = Layout.UNSET;

                } else {
                    throw new IllegalArgumentException("Left to "
                            + sideToString(endSide) + " undefined");
                }
                constraint.layout.leftMargin = margin;
                break;
            case RIGHT:
                if (endSide == LEFT) {
                    constraint.layout.rightToLeft = endID;
                    constraint.layout.rightToRight = Layout.UNSET;

                } else if (endSide == RIGHT) {
                    constraint.layout.rightToRight = endID;
                    constraint.layout.rightToLeft = Layout.UNSET;

                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                constraint.layout.rightMargin = margin;
                break;
            case TOP:
                if (endSide == TOP) {
                    constraint.layout.topToTop = endID;
                    constraint.layout.topToBottom = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else if (endSide == BOTTOM) {
                    constraint.layout.topToBottom = endID;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                constraint.layout.topMargin = margin;
                break;
            case BOTTOM:
                if (endSide == BOTTOM) {
                    constraint.layout.bottomToBottom = endID;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else if (endSide == TOP) {
                    constraint.layout.bottomToTop = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                constraint.layout.bottomMargin = margin;
                break;
            case BASELINE:
                if (endSide == BASELINE) {
                    constraint.layout.baselineToBaseline = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.topToBottom = Layout.UNSET;
                } else if (endSide == TOP) {
                    constraint.layout.baselineToTop = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.topToBottom = Layout.UNSET;
                } else if (endSide == BOTTOM) {
                    constraint.layout.baselineToBottom = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.topToBottom = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            case START:
                if (endSide == START) {
                    constraint.layout.startToStart = endID;
                    constraint.layout.startToEnd = Layout.UNSET;
                } else if (endSide == END) {
                    constraint.layout.startToEnd = endID;
                    constraint.layout.startToStart = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                constraint.layout.startMargin = margin;
                break;
            case END:
                if (endSide == END) {
                    constraint.layout.endToEnd = endID;
                    constraint.layout.endToStart = Layout.UNSET;
                } else if (endSide == START) {
                    constraint.layout.endToStart = endID;
                    constraint.layout.endToEnd = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                constraint.layout.endMargin = margin;
                break;
            default:
                throw new IllegalArgumentException(
                        sideToString(startSide) + " to " + sideToString(endSide) + " unknown");
        }
    }

    /**
     * Create a constraint between two widgets.
     * (for sides see: {@link #TOP}, {@link #BOTTOM}, {@link #START},
     * {@link #END}, {@link #LEFT}, {@link #RIGHT}, {@link #BASELINE})
     *
     * @param startID   the ID of the widget to be constrained
     * @param startSide the side of the widget to constrain
     * @param endID     the id of the widget to constrain to
     * @param endSide   the side of widget to constrain to
     */
    public void connect(int startID, int startSide, int endID, int endSide) {
        if (!mConstraints.containsKey(startID)) {
            mConstraints.put(startID, new Constraint());
        }
        Constraint constraint = mConstraints.get(startID);
        if (constraint == null) {
            return;
        }
        switch (startSide) {
            case LEFT:
                if (endSide == LEFT) {
                    constraint.layout.leftToLeft = endID;
                    constraint.layout.leftToRight = Layout.UNSET;
                } else if (endSide == RIGHT) {
                    constraint.layout.leftToRight = endID;
                    constraint.layout.leftToLeft = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("left to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            case RIGHT:
                if (endSide == LEFT) {
                    constraint.layout.rightToLeft = endID;
                    constraint.layout.rightToRight = Layout.UNSET;

                } else if (endSide == RIGHT) {
                    constraint.layout.rightToRight = endID;
                    constraint.layout.rightToLeft = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            case TOP:
                if (endSide == TOP) {
                    constraint.layout.topToTop = endID;
                    constraint.layout.topToBottom = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else if (endSide == BOTTOM) {
                    constraint.layout.topToBottom = endID;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            case BOTTOM:
                if (endSide == BOTTOM) {
                    constraint.layout.bottomToBottom = endID;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else if (endSide == TOP) {
                    constraint.layout.bottomToTop = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            case BASELINE:
                if (endSide == BASELINE) {
                    constraint.layout.baselineToBaseline = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.topToBottom = Layout.UNSET;
                } else if (endSide == TOP) {
                    constraint.layout.baselineToTop = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.topToBottom = Layout.UNSET;
                } else if (endSide == BOTTOM) {
                    constraint.layout.baselineToBottom = endID;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.topToBottom = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            case START:
                if (endSide == START) {
                    constraint.layout.startToStart = endID;
                    constraint.layout.startToEnd = Layout.UNSET;
                } else if (endSide == END) {
                    constraint.layout.startToEnd = endID;
                    constraint.layout.startToStart = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            case END:
                if (endSide == END) {
                    constraint.layout.endToEnd = endID;
                    constraint.layout.endToStart = Layout.UNSET;
                } else if (endSide == START) {
                    constraint.layout.endToStart = endID;
                    constraint.layout.endToEnd = Layout.UNSET;
                } else {
                    throw new IllegalArgumentException("right to "
                            + sideToString(endSide) + " undefined");
                }
                break;
            default:
                throw new IllegalArgumentException(
                        sideToString(startSide) + " to " + sideToString(endSide) + " unknown");
        }
    }

    /**
     * Centers the view horizontally relative to toView's position.
     *
     * @param viewId ID of view to center Horizontally
     * @param toView ID of view to center on (or in)
     */
    public void centerHorizontally(int viewId, int toView) {
        if (toView == PARENT_ID) {
            center(viewId, PARENT_ID, ConstraintSet.LEFT, 0, PARENT_ID,
                    ConstraintSet.RIGHT, 0, 0.5f);
        } else {
            center(viewId, toView, ConstraintSet.RIGHT, 0, toView,
                    ConstraintSet.LEFT, 0, 0.5f);
        }
    }

    /**
     * Centers the view horizontally relative to toView's position.
     *
     * @param viewId ID of view to center Horizontally
     * @param toView ID of view to center on (or in)
     */
    public void centerHorizontallyRtl(int viewId, int toView) {
        if (toView == PARENT_ID) {
            center(viewId, PARENT_ID, ConstraintSet.START, 0, PARENT_ID,
                    ConstraintSet.END, 0, 0.5f);
        } else {
            center(viewId, toView, ConstraintSet.END, 0, toView,
                    ConstraintSet.START, 0, 0.5f);
        }
    }

    /**
     * Centers the view vertically relative to toView's position.
     *
     * @param viewId ID of view to center Horizontally
     * @param toView ID of view to center on (or in)
     */
    public void centerVertically(int viewId, int toView) {
        if (toView == PARENT_ID) {
            center(viewId, PARENT_ID, ConstraintSet.TOP, 0, PARENT_ID,
                    ConstraintSet.BOTTOM, 0, 0.5f);
        } else {
            center(viewId, toView, ConstraintSet.BOTTOM, 0, toView, ConstraintSet.TOP,
                    0, 0.5f);
        }
    }

    /**
     * Remove all constraints from this view.
     *
     * @param viewId ID of view to remove all connections to
     */
    public void clear(int viewId) {
        mConstraints.remove(viewId);
    }

    /**
     * Remove a constraint from this view.
     *
     * @param viewId ID of view to center on (or in)
     * @param anchor the Anchor to remove constraint from
     */
    public void clear(int viewId, int anchor) {
        if (mConstraints.containsKey(viewId)) {
            Constraint constraint = mConstraints.get(viewId);
            if (constraint == null) {
                return;
            }
            switch (anchor) {
                case LEFT:
                    constraint.layout.leftToRight = Layout.UNSET;
                    constraint.layout.leftToLeft = Layout.UNSET;
                    constraint.layout.leftMargin = Layout.UNSET;
                    constraint.layout.goneLeftMargin = Layout.UNSET_GONE_MARGIN;
                    break;
                case RIGHT:
                    constraint.layout.rightToRight = Layout.UNSET;
                    constraint.layout.rightToLeft = Layout.UNSET;
                    constraint.layout.rightMargin = Layout.UNSET;
                    constraint.layout.goneRightMargin = Layout.UNSET_GONE_MARGIN;
                    break;
                case TOP:
                    constraint.layout.topToBottom = Layout.UNSET;
                    constraint.layout.topToTop = Layout.UNSET;
                    constraint.layout.topMargin = 0;
                    constraint.layout.goneTopMargin = Layout.UNSET_GONE_MARGIN;
                    break;
                case BOTTOM:
                    constraint.layout.bottomToTop = Layout.UNSET;
                    constraint.layout.bottomToBottom = Layout.UNSET;
                    constraint.layout.bottomMargin = 0;
                    constraint.layout.goneBottomMargin = Layout.UNSET_GONE_MARGIN;
                    break;
                case BASELINE:
                    constraint.layout.baselineToBaseline = Layout.UNSET;
                    constraint.layout.baselineToTop = Layout.UNSET;
                    constraint.layout.baselineToBottom = Layout.UNSET;
                    constraint.layout.baselineMargin = 0;
                    constraint.layout.goneBaselineMargin = Layout.UNSET_GONE_MARGIN;
                    break;
                case START:
                    constraint.layout.startToEnd = Layout.UNSET;
                    constraint.layout.startToStart = Layout.UNSET;
                    constraint.layout.startMargin = 0;
                    constraint.layout.goneStartMargin = Layout.UNSET_GONE_MARGIN;
                    break;
                case END:
                    constraint.layout.endToStart = Layout.UNSET;
                    constraint.layout.endToEnd = Layout.UNSET;
                    constraint.layout.endMargin = 0;
                    constraint.layout.goneEndMargin = Layout.UNSET_GONE_MARGIN;
                    break;
                case CIRCLE_REFERENCE:
                    constraint.layout.circleAngle = Layout.UNSET;
                    constraint.layout.circleRadius = Layout.UNSET;
                    constraint.layout.circleConstraint = Layout.UNSET;
                    break;
                default:
                    throw new IllegalArgumentException("unknown constraint");
            }
        }
    }

    /**
     * Sets the margin.
     *
     * @param viewId ID of view to adjust the margin on
     * @param anchor The side to adjust the margin on
     * @param value  The new value for the margin
     */
    public void setMargin(int viewId, int anchor, int value) {
        Constraint constraint = get(viewId);
        switch (anchor) {
            case LEFT:
                constraint.layout.leftMargin = value;
                break;
            case RIGHT:
                constraint.layout.rightMargin = value;
                break;
            case TOP:
                constraint.layout.topMargin = value;
                break;
            case BOTTOM:
                constraint.layout.bottomMargin = value;
                break;
            case BASELINE:
                constraint.layout.baselineMargin = value;
                break;
            case START:
                constraint.layout.startMargin = value;
                break;
            case END:
                constraint.layout.endMargin = value;
                break;
            default:
                throw new IllegalArgumentException("unknown constraint");
        }
    }

    /**
     * Sets the gone margin.
     *
     * @param viewId ID of view to adjust the margin on
     * @param anchor The side to adjust the margin on
     * @param value  The new value for the margin
     */
    public void setGoneMargin(int viewId, int anchor, int value) {
        Constraint constraint = get(viewId);
        switch (anchor) {
            case LEFT:
                constraint.layout.goneLeftMargin = value;
                break;
            case RIGHT:
                constraint.layout.goneRightMargin = value;
                break;
            case TOP:
                constraint.layout.goneTopMargin = value;
                break;
            case BOTTOM:
                constraint.layout.goneBottomMargin = value;
                break;
            case BASELINE:
                constraint.layout.goneBaselineMargin = value;
                break;
            case START:
                constraint.layout.goneStartMargin = value;
                break;
            case END:
                constraint.layout.goneEndMargin = value;
                break;
            default:
                throw new IllegalArgumentException("unknown constraint");
        }
    }

    /**
     * Adjust the horizontal bias of the view (used with views constrained on left and right).
     *
     * @param viewId ID of view to adjust the horizontal
     * @param bias   the new bias 0.5 is in the middle
     */
    public void setHorizontalBias(int viewId, float bias) {
        get(viewId).layout.horizontalBias = bias;
    }

    /**
     * Adjust the vertical bias of the view (used with views constrained on left and right).
     *
     * @param viewId ID of view to adjust the vertical
     * @param bias   the new bias 0.5 is in the middle
     */
    public void setVerticalBias(int viewId, float bias) {
        get(viewId).layout.verticalBias = bias;
    }

    /**
     * Constrains the views aspect ratio.
     * For Example a HD screen is 16 by 9 = 16/(float)9 = 1.777f.
     *
     * @param viewId ID of view to constrain
     * @param ratio  The ratio of the width to height (width / height)
     */
    public void setDimensionRatio(int viewId, String ratio) {
        get(viewId).layout.dimensionRatio = ratio;
    }

    /**
     * Adjust the visibility of a view.
     *
     * @param viewId     ID of view to adjust the vertical
     * @param visibility the visibility
     */
    public void setVisibility(int viewId, int visibility) {
        get(viewId).propertySet.visibility = visibility;
    }

    /**
     * ConstraintSet will not setVisibility. {@link #VISIBILITY_MODE_IGNORE} or {@link
     * #VISIBILITY_MODE_NORMAL}.
     *
     * @param viewId         ID of view
     * @param visibilityMode
     */
    public void setVisibilityMode(int viewId, int visibilityMode) {
        get(viewId).propertySet.mVisibilityMode = visibilityMode;
    }

    /**
     * ConstraintSet will not setVisibility. {@link #VISIBILITY_MODE_IGNORE} or {@link
     * #VISIBILITY_MODE_NORMAL}.
     *
     * @param viewId ID of view
     */
    public int getVisibilityMode(int viewId) {
        return get(viewId).propertySet.mVisibilityMode;
    }

    /**
     * Get the visibility flag set in this view
     *
     * @param viewId the id of the view
     * @return the visibility constraint for the view
     */
    public int getVisibility(int viewId) {
        return get(viewId).propertySet.visibility;
    }

    /**
     * Get the height set in the view
     *
     * @param viewId the id of the view
     * @return return the height constraint of the view
     */
    public int getHeight(int viewId) {
        return get(viewId).layout.mHeight;
    }

    /**
     * Get the width set in the view
     *
     * @param viewId the id of the view
     * @return return the width constraint of the view
     */
    public int getWidth(int viewId) {
        return get(viewId).layout.mWidth;
    }

    /**
     * Adjust the alpha of a view.
     *
     * @param viewId ID of view to adjust the vertical
     * @param alpha  the alpha
     */
    public void setAlpha(int viewId, float alpha) {
        get(viewId).propertySet.alpha = alpha;
    }

    /**
     * return with the constraint set will apply elevation for the specified view.
     *
     * @return true if the elevation will be set on this view (default is false)
     */
    public boolean getApplyElevation(int viewId) {
        return get(viewId).transform.applyElevation;
    }

    /**
     * set if elevation will be applied to the view.
     * Elevation logic is based on style and animation. By default it is not used because it would
     * lead to unexpected results.
     *
     * @param apply true if this constraint set applies elevation to this view
     */
    public void setApplyElevation(int viewId, boolean apply) {
        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            get(viewId).transform.applyElevation = apply;
        }
    }

    /**
     * Adjust the elevation of a view.
     *
     * @param viewId    ID of view to adjust the elevation
     * @param elevation the elevation
     */
    public void setElevation(int viewId, float elevation) {
        if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
            get(viewId).transform.elevation = elevation;
            get(viewId).transform.applyElevation = true;
        }
    }

    /**
     * Adjust the post-layout rotation about the Z axis of a view.
     *
     * @param viewId   ID of view to adjust th Z rotation
     * @param rotation the rotation about the X axis
     */
    public void setRotation(int viewId, float rotation) {
        get(viewId).transform.rotation = rotation;
    }

    /**
     * Adjust the post-layout rotation about the X axis of a view.
     *
     * @param viewId    ID of view to adjust th X rotation
     * @param rotationX the rotation about the X axis
     */
    public void setRotationX(int viewId, float rotationX) {
        get(viewId).transform.rotationX = rotationX;
    }

    /**
     * Adjust the post-layout rotation about the Y axis of a view.
     *
     * @param viewId    ID of view to adjust the Y rotation
     * @param rotationY the rotationY
     */
    public void setRotationY(int viewId, float rotationY) {
        get(viewId).transform.rotationY = rotationY;
    }

    /**
     * Adjust the post-layout scale in X of a view.
     *
     * @param viewId ID of view to adjust the scale in X
     * @param scaleX the scale in X
     */
    public void setScaleX(int viewId, float scaleX) {
        get(viewId).transform.scaleX = scaleX;
    }

    /**
     * Adjust the post-layout scale in Y of a view.
     *
     * @param viewId ID of view to adjust the scale in Y
     * @param scaleY the scale in Y
     */
    public void setScaleY(int viewId, float scaleY) {
        get(viewId).transform.scaleY = scaleY;
    }

    /**
     * Set X location of the pivot point around which the view will rotate and scale.
     * use Float.NaN to clear the pivot value.
     * Note: once an actual View has had its pivot set it cannot be cleared.
     *
     * @param viewId          ID of view to adjust the transforms pivot point about X
     * @param transformPivotX X location of the pivot point.
     */
    public void setTransformPivotX(int viewId, float transformPivotX) {
        get(viewId).transform.transformPivotX = transformPivotX;
    }

    /**
     * Set Y location of the pivot point around which the view will rotate and scale.
     * use Float.NaN to clear the pivot value.
     * Note: once an actual View has had its pivot set it cannot be cleared.
     *
     * @param viewId          ID of view to adjust the transforms pivot point about Y
     * @param transformPivotY Y location of the pivot point.
     */
    public void setTransformPivotY(int viewId, float transformPivotY) {
        get(viewId).transform.transformPivotY = transformPivotY;
    }

    /**
     * Set X,Y location of the pivot point around which the view will rotate and scale.
     * use Float.NaN to clear the pivot value.
     * Note: once an actual View has had its pivot set it cannot be cleared.
     *
     * @param viewId          ID of view to adjust the transforms pivot point
     * @param transformPivotX X location of the pivot point.
     * @param transformPivotY Y location of the pivot point.
     */
    public void setTransformPivot(int viewId, float transformPivotX, float transformPivotY) {
        Constraint constraint = get(viewId);
        constraint.transform.transformPivotY = transformPivotY;
        constraint.transform.transformPivotX = transformPivotX;
    }

    /**
     * Adjust the post-layout X translation of a view.
     *
     * @param viewId       ID of view to translate in X
     * @param translationX the translation in X
     */
    public void setTranslationX(int viewId, float translationX) {
        get(viewId).transform.translationX = translationX;
    }

    /**
     * Adjust the  post-layout Y translation of a view.
     *
     * @param viewId       ID of view to to translate in Y
     * @param translationY the translation in Y
     */
    public void setTranslationY(int viewId, float translationY) {
        get(viewId).transform.translationY = translationY;
    }

    /**
     * Adjust the post-layout translation of a view.
     *
     * @param viewId       ID of view to adjust its translation in X & Y
     * @param translationX the translation in X
     * @param translationY the translation in Y
     */
    public void setTranslation(int viewId, float translationX, float translationY) {
        Constraint constraint = get(viewId);
        constraint.transform.translationX = translationX;
        constraint.transform.translationY = translationY;
    }

    /**
     * Adjust the translation in Z of a view.
     *
     * @param viewId       ID of view to adjust
     * @param translationZ the translationZ
     */
    public void setTranslationZ(int viewId, float translationZ) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            get(viewId).transform.translationZ = translationZ;
        }
    }

    /**
     *
     */
    public void setEditorAbsoluteX(int viewId, int position) {
        get(viewId).layout.editorAbsoluteX = position;
    }

    /**
     *
     */
    public void setEditorAbsoluteY(int viewId, int position) {
        get(viewId).layout.editorAbsoluteY = position;
    }

    /**
     * Sets the wrap behavior of the widget in the parent's wrap computation
     */
    public void setLayoutWrapBehavior(int viewId, int behavior) {
        if (behavior >= 0 && behavior <= ConstraintWidget.WRAP_BEHAVIOR_SKIPPED) {
            get(viewId).layout.mWrapBehavior = behavior;
        }
    }

    /**
     * Sets the height of the view. It can be a dimension, {@link #WRAP_CONTENT} or {@link
     * #MATCH_CONSTRAINT}.
     *
     * @param viewId ID of view to adjust its height
     * @param height the height of the view
     */
    public void constrainHeight(int viewId, int height) {
        get(viewId).layout.mHeight = height;
    }

    /**
     * Sets the width of the view. It can be a dimension, {@link #WRAP_CONTENT} or {@link
     * #MATCH_CONSTRAINT}.
     *
     * @param viewId ID of view to adjust its width
     * @param width  the width of the view
     */
    public void constrainWidth(int viewId, int width) {
        get(viewId).layout.mWidth = width;
    }

    /**
     * Constrain the view on a circle constraint
     *
     * @param viewId ID of the view we constrain
     * @param id     ID of the view we constrain relative to
     * @param radius the radius of the circle in degrees
     * @param angle  the angle
     */
    public void constrainCircle(int viewId, int id, int radius, float angle) {
        Constraint constraint = get(viewId);
        constraint.layout.circleConstraint = id;
        constraint.layout.circleRadius = radius;
        constraint.layout.circleAngle = angle;
    }

    /**
     * Sets the maximum height of the view. It is a dimension, It is only applicable if height is
     * #MATCH_CONSTRAINT}.
     *
     * @param viewId ID of view to adjust it height
     * @param height the maximum height of the constraint
     */
    public void constrainMaxHeight(int viewId, int height) {
        get(viewId).layout.heightMax = height;
    }

    /**
     * Sets the maximum width of the view. It is a dimension, It is only applicable if width is
     * #MATCH_CONSTRAINT}.
     *
     * @param viewId ID of view to adjust its max height
     * @param width  the maximum width of the view
     */
    public void constrainMaxWidth(int viewId, int width) {
        get(viewId).layout.widthMax = width;
    }

    /**
     * Sets the height of the view. It is a dimension, It is only applicable if height is
     * #MATCH_CONSTRAINT}.
     *
     * @param viewId ID of view to adjust its min height
     * @param height the minimum height of the view
     */
    public void constrainMinHeight(int viewId, int height) {
        get(viewId).layout.heightMin = height;
    }

    /**
     * Sets the width of the view.  It is a dimension, It is only applicable if width is
     * #MATCH_CONSTRAINT}.
     *
     * @param viewId ID of view to adjust its min height
     * @param width  the minimum width of the view
     */
    public void constrainMinWidth(int viewId, int width) {
        get(viewId).layout.widthMin = width;
    }

    /**
     * Sets the width of the view as a percentage of the parent.
     *
     * @param viewId
     * @param percent
     */
    public void constrainPercentWidth(int viewId, float percent) {
        get(viewId).layout.widthPercent = percent;
    }

    /**
     * Sets the height of the view as a percentage of the parent.
     *
     * @param viewId
     * @param percent
     */
    public void constrainPercentHeight(int viewId, float percent) {
        get(viewId).layout.heightPercent = percent;
    }

    /**
     * Sets how the height is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.
     * Default is spread.
     *
     * @param viewId ID of view to adjust its matchConstraintDefaultHeight
     * @param height MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD
     */
    public void constrainDefaultHeight(int viewId, int height) {
        get(viewId).layout.heightDefault = height;
    }

    /**
     * Sets how the width is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.
     * Default is spread.
     *
     * @param viewId      ID of view to adjust its matchConstraintDefaultWidth
     * @param constrained if true with will be constrained
     */
    public void constrainedWidth(int viewId, boolean constrained) {
        get(viewId).layout.constrainedWidth = constrained;
    }

    /**
     * Sets how the height is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.
     * Default is spread.
     *
     * @param viewId      ID of view to adjust its matchConstraintDefaultHeight
     * @param constrained if true height will be constrained
     */
    public void constrainedHeight(int viewId, boolean constrained) {
        get(viewId).layout.constrainedHeight = constrained;
    }

    /**
     * Sets how the width is calculated ether MATCH_CONSTRAINT_WRAP or MATCH_CONSTRAINT_SPREAD.
     * Default is spread.
     *
     * @param viewId ID of view to adjust its matchConstraintDefaultWidth
     * @param width  SPREAD or WRAP
     */
    public void constrainDefaultWidth(int viewId, int width) {
        get(viewId).layout.widthDefault = width;
    }

    /**
     * The child's weight that we can use to distribute the available horizontal space
     * in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
     *
     * @param viewId ID of view to adjust its HorizontalWeight
     * @param weight the weight that we can use to distribute the horizontal space
     */
    public void setHorizontalWeight(int viewId, float weight) {
        get(viewId).layout.horizontalWeight = weight;
    }

    /**
     * The child's weight that we can use to distribute the available vertical space
     * in a chain, if the dimension behaviour is set to MATCH_CONSTRAINT
     *
     * @param viewId ID of view to adjust its VerticalWeight
     * @param weight the weight that we can use to distribute the vertical space
     */
    public void setVerticalWeight(int viewId, float weight) {
        get(viewId).layout.verticalWeight = weight;
    }

    /**
     * How the elements of the horizontal chain will be positioned. if the dimension
     * behaviour is set to MATCH_CONSTRAINT. The possible values are:
     *
     * <ul>
     * <li>{@link #CHAIN_SPREAD} -- the elements will be spread out</li>
     * <li>{@link #CHAIN_SPREAD_INSIDE} -- similar, but the endpoints of the chain will not
     * be spread out</li>
     * <li>{@link #CHAIN_PACKED} -- the elements of the chain will be packed together. The
     * horizontal bias attribute of the child will then affect the positioning of the packed
     * elements</li>
     * </ul>
     *
     * @param viewId     ID of view to adjust its HorizontalChainStyle
     * @param chainStyle the weight that we can use to distribute the horizontal space
     */
    public void setHorizontalChainStyle(int viewId, int chainStyle) {
        get(viewId).layout.horizontalChainStyle = chainStyle;
    }

    /**
     * How the elements of the vertical chain will be positioned. in a chain, if the dimension
     * behaviour is set to MATCH_CONSTRAINT
     *
     * <ul>
     * <li>{@link #CHAIN_SPREAD} -- the elements will be spread out</li>
     * <li>{@link #CHAIN_SPREAD_INSIDE} -- similar, but the endpoints of the chain will not
     * be spread out</li>
     * <li>{@link #CHAIN_PACKED} -- the elements of the chain will be packed together. The
     * vertical bias attribute of the child will then affect the positioning of the packed
     * elements</li>
     * </ul>
     *
     * @param viewId     ID of view to adjust its VerticalChainStyle
     * @param chainStyle the weight that we can use to distribute the horizontal space
     */
    public void setVerticalChainStyle(int viewId, int chainStyle) {
        get(viewId).layout.verticalChainStyle = chainStyle;
    }

    /**
     * Adds a view to a horizontal chain.
     *
     * @param viewId  view to add
     * @param leftId  view in chain to the left
     * @param rightId view in chain to the right
     */
    public void addToHorizontalChain(int viewId, int leftId, int rightId) {
        connect(viewId, LEFT, leftId, (leftId == PARENT_ID) ? LEFT : RIGHT, 0);
        connect(viewId, RIGHT, rightId, (rightId == PARENT_ID) ? RIGHT : LEFT, 0);
        if (leftId != PARENT_ID) {
            connect(leftId, RIGHT, viewId, LEFT, 0);
        }
        if (rightId != PARENT_ID) {
            connect(rightId, LEFT, viewId, RIGHT, 0);
        }
    }

    /**
     * Adds a view to a horizontal chain.
     *
     * @param viewId  view to add
     * @param leftId  view to the start side
     * @param rightId view to the end side
     */
    public void addToHorizontalChainRTL(int viewId, int leftId, int rightId) {
        connect(viewId, START, leftId, (leftId == PARENT_ID) ? START : END, 0);
        connect(viewId, END, rightId, (rightId == PARENT_ID) ? END : START, 0);
        if (leftId != PARENT_ID) {
            connect(leftId, END, viewId, START, 0);
        }
        if (rightId != PARENT_ID) {
            connect(rightId, START, viewId, END, 0);
        }
    }

    /**
     * Adds a view to a vertical chain.
     *
     * @param viewId   view to add to a vertical chain
     * @param topId    view above.
     * @param bottomId view below
     */
    public void addToVerticalChain(int viewId, int topId, int bottomId) {
        connect(viewId, TOP, topId, (topId == PARENT_ID) ? TOP : BOTTOM, 0);
        connect(viewId, BOTTOM, bottomId, (bottomId == PARENT_ID) ? BOTTOM : TOP, 0);
        if (topId != PARENT_ID) {
            connect(topId, BOTTOM, viewId, TOP, 0);
        }
        if (bottomId != PARENT_ID) {
            connect(bottomId, TOP, viewId, BOTTOM, 0);
        }
    }

    /**
     * Removes a view from a vertical chain.
     * This assumes the view is connected to a vertical chain.
     * Its behaviour is undefined if not part of a vertical chain.
     *
     * @param viewId the view to be removed
     */
    public void removeFromVerticalChain(int viewId) {
        if (mConstraints.containsKey(viewId)) {
            Constraint constraint = mConstraints.get(viewId);
            if (constraint == null) {
                return;
            }
            int topId = constraint.layout.topToBottom;
            int bottomId = constraint.layout.bottomToTop;
            if (topId != Layout.UNSET || bottomId != Layout.UNSET) {
                if (topId != Layout.UNSET && bottomId != Layout.UNSET) {
                    // top and bottom connected to views
                    connect(topId, BOTTOM, bottomId, TOP, 0);
                    connect(bottomId, TOP, topId, BOTTOM, 0);
                } else if (constraint.layout.bottomToBottom != Layout.UNSET) {
                    // top connected to view. Bottom connected to parent
                    connect(topId, BOTTOM, constraint.layout.bottomToBottom, BOTTOM, 0);
                } else if (constraint.layout.topToTop != Layout.UNSET) {
                    // bottom connected to view. Top connected to parent
                    connect(bottomId, TOP, constraint.layout.topToTop, TOP, 0);
                }
            }
        }
        clear(viewId, TOP);
        clear(viewId, BOTTOM);
    }

    /**
     * Removes a view from a horizontal chain.
     * This assumes the view is connected to a horizontal chain.
     * Its behaviour is undefined if not part of a horizontal chain.
     *
     * @param viewId the view to be removed
     */
    public void removeFromHorizontalChain(int viewId) {
        if (mConstraints.containsKey(viewId)) {
            Constraint constraint = mConstraints.get(viewId);
            if (constraint == null) {
                return;
            }
            int leftId = constraint.layout.leftToRight;
            int rightId = constraint.layout.rightToLeft;
            if (leftId != Layout.UNSET || rightId != Layout.UNSET) {
                if (leftId != Layout.UNSET && rightId != Layout.UNSET) {
                    // left and right connected to views
                    connect(leftId, RIGHT, rightId, LEFT, 0);
                    connect(rightId, LEFT, leftId, RIGHT, 0);
                } else if (constraint.layout.rightToRight != Layout.UNSET) {
                    // left connected to view. right connected to parent
                    connect(leftId, RIGHT, constraint.layout.rightToRight, RIGHT, 0);
                } else if (constraint.layout.leftToLeft != Layout.UNSET) {
                    // right connected to view. left connected to parent
                    connect(rightId, LEFT, constraint.layout.leftToLeft, LEFT, 0);
                }
                clear(viewId, LEFT);
                clear(viewId, RIGHT);
            } else {

                int startId = constraint.layout.startToEnd;
                int endId = constraint.layout.endToStart;
                if (startId != Layout.UNSET || endId != Layout.UNSET) {
                    if (startId != Layout.UNSET && endId != Layout.UNSET) {
                        // start and end connected to views
                        connect(startId, END, endId, START, 0);
                        connect(endId, START, leftId, END, 0);
                    } else if (endId != Layout.UNSET) {
                        if (constraint.layout.rightToRight != Layout.UNSET) {
                            // left connected to view. right connected to parent
                            connect(leftId, END, constraint.layout.rightToRight, END, 0);
                        } else if (constraint.layout.leftToLeft != Layout.UNSET) {
                            // right connected to view. left connected to parent
                            connect(endId, START, constraint.layout.leftToLeft, START, 0);
                        }
                    }
                }
                clear(viewId, START);
                clear(viewId, END);
            }
        }
    }

    /**
     * Creates a ConstraintLayout virtual object. Currently only horizontal or vertical GuideLines.
     *
     * @param guidelineID ID of guideline to create
     * @param orientation the Orientation of the guideline
     */
    public void create(int guidelineID, int orientation) {
        Constraint constraint = get(guidelineID);
        constraint.layout.mIsGuideline = true;
        constraint.layout.orientation = orientation;
    }

    /**
     * Creates a ConstraintLayout Barrier object.
     *
     * @param id
     * @param direction  Barrier.{LEFT,RIGHT,TOP,BOTTOM,START,END}
     * @param referenced
     */
    public void createBarrier(int id, int direction, int margin, int... referenced) {
        Constraint constraint = get(id);
        constraint.layout.mHelperType = BARRIER_TYPE;
        constraint.layout.mBarrierDirection = direction;
        constraint.layout.mBarrierMargin = margin;
        constraint.layout.mIsGuideline = false;
        constraint.layout.mReferenceIds = referenced;
    }

    /**
     * Set the guideline's distance form the top or left edge.
     *
     * @param guidelineID ID of the guideline
     * @param margin      the distance to the top or left edge
     */
    public void setGuidelineBegin(int guidelineID, int margin) {
        get(guidelineID).layout.guideBegin = margin;
        get(guidelineID).layout.guideEnd = Layout.UNSET;
        get(guidelineID).layout.guidePercent = Layout.UNSET;

    }

    /**
     * Set a guideline's distance to end.
     *
     * @param guidelineID ID of the guideline
     * @param margin      the margin to the right or bottom side of container
     */
    public void setGuidelineEnd(int guidelineID, int margin) {
        get(guidelineID).layout.guideEnd = margin;
        get(guidelineID).layout.guideBegin = Layout.UNSET;
        get(guidelineID).layout.guidePercent = Layout.UNSET;
    }

    /**
     * Set a Guideline's percent.
     *
     * @param guidelineID ID of the guideline
     * @param ratio       the ratio between the gap on the left and right
     *                   0.0 is top/left 0.5 is middle
     */
    public void setGuidelinePercent(int guidelineID, float ratio) {
        get(guidelineID).layout.guidePercent = ratio;
        get(guidelineID).layout.guideEnd = Layout.UNSET;
        get(guidelineID).layout.guideBegin = Layout.UNSET;
    }

    /**
     * get the reference id's of a helper.
     *
     * @param id
     * @return array of id's
     */
    public int[] getReferencedIds(int id) {
        Constraint constraint = get(id);
        if (constraint.layout.mReferenceIds == null) {
            return new int[0];
        }
        return Arrays.copyOf(constraint.layout.mReferenceIds,
                constraint.layout.mReferenceIds.length);
    }

    /**
     * sets the reference id's of a barrier.
     *
     * @param id
     * @param referenced
     */
    public void setReferencedIds(int id, int... referenced) {
        Constraint constraint = get(id);
        constraint.layout.mReferenceIds = referenced;
    }

    /**
     * SEt tye type of barier
     * @param id
     * @param type
     */
    public void setBarrierType(int id, int type) {
        Constraint constraint = get(id);
        constraint.layout.mHelperType = type;
    }

    /**
     * Remove the attribute
     * @param attributeName
     */
    public void removeAttribute(String attributeName) {
        mSavedAttributes.remove(attributeName);
    }

    /**
     * Set the value of an attribute of type int
     * @param viewId
     * @param attributeName
     * @param value
     */
    public void setIntValue(int viewId, String attributeName, int value) {
        get(viewId).setIntValue(attributeName, value);
    }

    /**
     * Set the value of an attribute of type color
     * @param viewId
     * @param attributeName
     * @param value
     */
    public void setColorValue(int viewId, String attributeName, int value) {
        get(viewId).setColorValue(attributeName, value);
    }

    /**
     * Set the value of an attribute of type float
     * @param viewId
     * @param attributeName
     * @param value
     */
    public void setFloatValue(int viewId, String attributeName, float value) {
        get(viewId).setFloatValue(attributeName, value);
    }

    /**
     * Set the value of an attribute of type string
     * @param viewId
     * @param attributeName
     * @param value
     */
    public void setStringValue(int viewId, String attributeName, String value) {
        get(viewId).setStringValue(attributeName, value);
    }

    private void addAttributes(AttributeType attributeType, String... attributeName) {
        ConstraintAttribute constraintAttribute = null;
        for (int i = 0; i < attributeName.length; i++) {
            if (mSavedAttributes.containsKey(attributeName[i])) {
                constraintAttribute = mSavedAttributes.get(attributeName[i]);
                if (constraintAttribute == null) {
                    continue;
                }
                if (constraintAttribute.getType() != attributeType) {
                    throw new IllegalArgumentException(
                            "ConstraintAttribute is already a "
                                    + constraintAttribute.getType().name());
                }
            } else {
                constraintAttribute = new ConstraintAttribute(attributeName[i], attributeType);
                mSavedAttributes.put(attributeName[i], constraintAttribute);
            }
        }
    }

    /**
     * Parse int
     * @param set
     * @param attributes
     */
    public void parseIntAttributes(Constraint set, String attributes) {
        String[] sp = attributes.split(",");
        for (int i = 0; i < sp.length; i++) {
            String[] attr = sp[i].split("=");
            if (attr.length != 2) {
                Log.w(TAG, " Unable to parse " + sp[i]);
            } else {
                set.setFloatValue(attr[0], Integer.decode(attr[1]));
            }
        }
    }

    /**
     * Parse color
     * @param set
     * @param attributes
     */
    public void parseColorAttributes(Constraint set, String attributes) {
        String[] sp = attributes.split(",");
        for (int i = 0; i < sp.length; i++) {
            String[] attr = sp[i].split("=");
            if (attr.length != 2) {
                Log.w(TAG, " Unable to parse " + sp[i]);
            } else {
                set.setColorValue(attr[0], Color.parseColor(attr[1]));
            }
        }
    }

    /**
     * Parse floats
     * @param set
     * @param attributes
     */
    public void parseFloatAttributes(Constraint set, String attributes) {
        String[] sp = attributes.split(",");
        for (int i = 0; i < sp.length; i++) {
            String[] attr = sp[i].split("=");
            if (attr.length != 2) {
                Log.w(TAG, " Unable to parse " + sp[i]);
            } else {
                set.setFloatValue(attr[0], Float.parseFloat(attr[1]));
            }
        }
    }

    /**
     * Parse string
     * @param set
     * @param attributes
     */
    public void parseStringAttributes(Constraint set, String attributes) {
        String[] sp = splitString(attributes);
        for (int i = 0; i < sp.length; i++) {
            String[] attr = sp[i].split("=");
            Log.w(TAG, " Unable to parse " + sp[i]);
            set.setStringValue(attr[0], attr[1]);
        }
    }

    private static String[] splitString(String str) {
        char[] chars = str.toCharArray();
        ArrayList<String> list = new ArrayList<>();
        boolean inDouble = false;
        int start = 0;
        for (int i = 0; i < chars.length; i++) {
            if (chars[i] == ',' && !inDouble) {
                list.add(new String(chars, start, i - start));
                start = i + 1;
            } else if (chars[i] == '"') {
                inDouble = !inDouble;
            }
        }
        list.add(new String(chars, start, chars.length - start));
        return list.toArray(new String[list.size()]);
    }

    /**
     * Add attribute of type Int
     * @param attributeName
     */
    public void addIntAttributes(String... attributeName) {
        addAttributes(AttributeType.INT_TYPE, attributeName);
    }

    /**
     * Add attribute of type Color
     * @param attributeName
     */
    public void addColorAttributes(String... attributeName) {
        addAttributes(AttributeType.COLOR_TYPE, attributeName);
    }

    /**
     * Add attribute of type float
     * @param attributeName
     */
    public void addFloatAttributes(String... attributeName) {
        addAttributes(AttributeType.FLOAT_TYPE, attributeName);
    }

    /**
     * Add attribute of type string
     * @param attributeName
     */
    public void addStringAttributes(String... attributeName) {
        addAttributes(AttributeType.STRING_TYPE, attributeName);
    }

    private Constraint get(int id) {
        if (!mConstraints.containsKey(id)) {
            mConstraints.put(id, new Constraint());
        }
        return mConstraints.get(id);
    }

    private String sideToString(int side) {
        switch (side) {
            case LEFT:
                return "left";
            case RIGHT:
                return "right";
            case TOP:
                return "top";
            case BOTTOM:
                return "bottom";
            case BASELINE:
                return "baseline";
            case START:
                return "start";
            case END:
                return "end";
        }
        return "undefined";
    }

    /**
     * Load a constraint set from a constraintSet.xml file.
     * Note. Do NOT use this to load a layout file.
     * It will fail silently as there is no efficient way to differentiate.
     *
     * @param context    the context for the inflation
     * @param resourceId id of xml file in res/xml/
     */
    public void load(Context context, int resourceId) {
        Resources res = context.getResources();
        XmlPullParser parser = res.getXml(resourceId);
        try {
            for (int eventType = parser.getEventType();
                    eventType != XmlResourceParser.END_DOCUMENT;
                    eventType = parser.next()) {
                switch (eventType) {
                    case XmlResourceParser.START_DOCUMENT:
                    case XmlResourceParser.END_TAG:
                    case XmlResourceParser.TEXT:
                        break;
                    case XmlResourceParser.START_TAG:
                        String tagName = parser.getName();
                        Constraint constraint = fillFromAttributeList(context,
                                Xml.asAttributeSet(parser), false);
                        if (tagName.equalsIgnoreCase("Guideline")) {
                            constraint.layout.mIsGuideline = true;
                        }
                        if (DEBUG) {
                            Log.v(TAG, Debug.getLoc()
                                    + " cache " + Debug.getName(context, constraint.mViewId)
                                    + " " + constraint.mViewId);
                        }
                        mConstraints.put(constraint.mViewId, constraint);
                        break;
                }
            }
        } catch (XmlPullParserException e) {
            Log.e(TAG, "Error parsing resource: " + resourceId, e);
        } catch (IOException e) {
            Log.e(TAG, "Error parsing resource: " + resourceId, e);
        }
    }

    /**
     * Load a constraint set from a constraintSet.xml file
     *
     * @param context the context for the inflation
     * @param parser  id of xml file in res/xml/
     */
    public void load(Context context, XmlPullParser parser) {
        String tagName = null;
        try {
            Constraint constraint = null;
            for (int eventType = parser.getEventType();
                    eventType != XmlResourceParser.END_DOCUMENT;
                    eventType = parser.next()) {
                switch (eventType) {
                    case XmlResourceParser.START_DOCUMENT:
                        String document = parser.getName();
                        break;
                    case XmlResourceParser.START_TAG:
                        tagName = parser.getName();
                        if (DEBUG) {
                            Log.v(TAG, Debug.getLoc() + " " + document + " tagName=" + tagName);
                        }
                        switch (tagName) {
                            case "Constraint":
                                constraint = fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser), false);
                                break;
                            case "ConstraintOverride":
                                constraint = fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser), true);
                                break;
                            case "Guideline":
                                constraint = fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser), false);
                                constraint.layout.mIsGuideline = true;
                                constraint.layout.mApply = true;
                                break;
                            case "Barrier":
                                constraint = fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser), false);
                                constraint.layout.mHelperType = BARRIER_TYPE;
                                break;
                            case "PropertySet":
                                if (constraint == null) {
                                    throw new RuntimeException(ERROR_MESSAGE
                                            + parser.getLineNumber());
                                }
                                constraint.propertySet.fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser));
                                break;
                            case "Transform":
                                if (constraint == null) {
                                    throw new RuntimeException(ERROR_MESSAGE
                                            + parser.getLineNumber());
                                }
                                constraint.transform.fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser));
                                break;
                            case "Layout":
                                if (constraint == null) {
                                    throw new RuntimeException(ERROR_MESSAGE
                                            + parser.getLineNumber());
                                }
                                constraint.layout.fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser));
                                break;
                            case "Motion":
                                if (constraint == null) {
                                    throw new RuntimeException(ERROR_MESSAGE
                                            + parser.getLineNumber());
                                }
                                constraint.motion.fillFromAttributeList(context,
                                        Xml.asAttributeSet(parser));
                                break;
                            case "CustomAttribute":
                            case "CustomMethod":
                                if (constraint == null) {
                                    throw new RuntimeException(ERROR_MESSAGE
                                            + parser.getLineNumber());
                                }
                                ConstraintAttribute.parse(context, parser,
                                        constraint.mCustomConstraints);
                                break;
                        }
//                        if (tagName.equalsIgnoreCase("Constraint")) {
//                            constraint = fillFromAttributeList(context,
//                            Xml.asAttributeSet(parser));
//                        } else if (tagName.equalsIgnoreCase("Guideline")) {
//                            constraint = fillFromAttributeList(context,
//                            Xml.asAttributeSet(parser));
//                            constraint.layout.mIsGuideline = true;
//                        } else if (tagName.equalsIgnoreCase("CustomAttribute")) {
//                            ConstraintAttribute.parse(context, parser,
//                            constraint.mCustomConstraints);
//                        }
                        break;
                    case XmlResourceParser.END_TAG:
                        tagName = parser.getName();
                        switch (tagName.toLowerCase(Locale.ROOT)) {
                            case "constraintset":
                                return;
                            case "constraint":
                            case "constraintoverride":
                            case "guideline":
                                mConstraints.put(constraint.mViewId, constraint);
                                constraint = null;
                        }
                        tagName = null;
                        break;
                    case XmlResourceParser.TEXT:
                        break;
                }
            }
        } catch (XmlPullParserException e) {
            Log.e(TAG, "Error parsing XML resource", e);
        } catch (IOException e) {
            Log.e(TAG, "Error parsing XML resource", e);
        }
    }

    private static int lookupID(TypedArray a, int index, int def) {
        int ret = a.getResourceId(index, def);
        if (ret == Layout.UNSET) {
            ret = a.getInt(index, Layout.UNSET);
        }
        return ret;
    }

    private Constraint fillFromAttributeList(Context context,
                                             AttributeSet attrs,
                                             boolean override) {
        Constraint c = new Constraint();
        TypedArray a = context.obtainStyledAttributes(attrs,
                override ? R.styleable.ConstraintOverride : R.styleable.Constraint);
        populateConstraint(c, a, override);
        a.recycle();
        return c;
    }

    /**
     * Used to read a ConstraintDelta
     *
     * @param context
     * @param parser
     * @return
     */
    public static Constraint buildDelta(Context context, XmlPullParser parser) {
        AttributeSet attrs = Xml.asAttributeSet(parser);
        Constraint c = new Constraint();
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ConstraintOverride);
        populateOverride(c, a);
        a.recycle();
        return c;
    }

    private static void populateOverride(Constraint c, TypedArray a) {

        final int count = a.getIndexCount();
        TypedValue type;
        Constraint.Delta delta = new Constraint.Delta();
        c.mDelta = delta;
        c.motion.mApply = false;
        c.layout.mApply = false;
        c.propertySet.mApply = false;
        c.transform.mApply = false;
        for (int i = 0; i < count; i++) {
            int attr = a.getIndex(i);


            int attrType = sOverrideMapToConstant.get(attr);
            if (DEBUG) {
                Log.v(TAG, Debug.getLoc() + " > " + attrType + " " + getDebugName(attrType));
            }

            switch (attrType) {

                case EDITOR_ABSOLUTE_X:
                    delta.add(EDITOR_ABSOLUTE_X,
                            a.getDimensionPixelOffset(attr, c.layout.editorAbsoluteX));
                    break;
                case EDITOR_ABSOLUTE_Y:
                    delta.add(EDITOR_ABSOLUTE_Y,
                            a.getDimensionPixelOffset(attr, c.layout.editorAbsoluteY));
                    break;
                case GUIDE_BEGIN:
                    delta.add(GUIDE_BEGIN, a.getDimensionPixelOffset(attr, c.layout.guideBegin));
                    break;
                case GUIDE_END:
                    delta.add(GUIDE_END, a.getDimensionPixelOffset(attr, c.layout.guideEnd));
                    break;
                case GUIDE_PERCENT:
                    delta.add(GUIDE_PERCENT, a.getFloat(attr, c.layout.guidePercent));
                    break;
                case GUIDELINE_USE_RTL:
                    delta.add(GUIDELINE_USE_RTL, a.getBoolean(attr, c.layout.guidelineUseRtl));
                    break;
                case ORIENTATION:
                    delta.add(ORIENTATION, a.getInt(attr, c.layout.orientation));
                    break;
                case CIRCLE_RADIUS:
                    delta.add(CIRCLE_RADIUS, a.getDimensionPixelSize(attr, c.layout.circleRadius));
                    break;
                case CIRCLE_ANGLE:
                    delta.add(CIRCLE_ANGLE, a.getFloat(attr, c.layout.circleAngle));
                    break;
                case GONE_LEFT_MARGIN:
                    delta.add(GONE_LEFT_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.goneLeftMargin));
                    break;
                case GONE_TOP_MARGIN:
                    delta.add(GONE_TOP_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.goneTopMargin));
                    break;
                case GONE_RIGHT_MARGIN:
                    delta.add(GONE_RIGHT_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.goneRightMargin));
                    break;
                case GONE_BOTTOM_MARGIN:
                    delta.add(GONE_BOTTOM_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.goneBottomMargin));
                    break;
                case GONE_START_MARGIN:
                    delta.add(GONE_START_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.goneStartMargin));
                    break;
                case GONE_END_MARGIN:
                    delta.add(GONE_END_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.goneEndMargin));
                    break;
                case GONE_BASELINE_MARGIN:
                    delta.add(GONE_BASELINE_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.goneBaselineMargin));
                    break;
                case HORIZONTAL_BIAS:
                    delta.add(HORIZONTAL_BIAS, a.getFloat(attr, c.layout.horizontalBias));
                    break;
                case VERTICAL_BIAS:
                    delta.add(VERTICAL_BIAS, a.getFloat(attr, c.layout.verticalBias));
                    break;
                case LEFT_MARGIN:
                    delta.add(LEFT_MARGIN, a.getDimensionPixelSize(attr, c.layout.leftMargin));
                    break;
                case RIGHT_MARGIN:
                    delta.add(RIGHT_MARGIN, a.getDimensionPixelSize(attr, c.layout.rightMargin));
                    break;
                case START_MARGIN:
                    delta.add(START_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.startMargin));
                    break;
                case END_MARGIN:
                    delta.add(END_MARGIN, a.getDimensionPixelSize(attr, c.layout.endMargin));
                    break;
                case TOP_MARGIN:
                    delta.add(TOP_MARGIN, a.getDimensionPixelSize(attr, c.layout.topMargin));
                    break;
                case BOTTOM_MARGIN:
                    delta.add(BOTTOM_MARGIN, a.getDimensionPixelSize(attr, c.layout.bottomMargin));
                    break;
                case BASELINE_MARGIN:
                    delta.add(BASELINE_MARGIN,
                            a.getDimensionPixelSize(attr, c.layout.baselineMargin));
                    break;
                case LAYOUT_WIDTH:
                    delta.add(LAYOUT_WIDTH, a.getLayoutDimension(attr, c.layout.mWidth));
                    break;
                case LAYOUT_HEIGHT:
                    delta.add(LAYOUT_HEIGHT, a.getLayoutDimension(attr, c.layout.mHeight));
                    break;
                case LAYOUT_CONSTRAINT_WIDTH:
                    ConstraintSet.parseDimensionConstraints(delta, a, attr, HORIZONTAL);
                    break;
                case LAYOUT_CONSTRAINT_HEIGHT:
                    ConstraintSet.parseDimensionConstraints(delta, a, attr, VERTICAL);
                    break;
                case LAYOUT_WRAP_BEHAVIOR:
                    delta.add(LAYOUT_WRAP_BEHAVIOR, a.getInt(attr, c.layout.mWrapBehavior));
                    break;
                case WIDTH_DEFAULT:
                    delta.add(WIDTH_DEFAULT, a.getInt(attr, c.layout.widthDefault));
                    break;
                case HEIGHT_DEFAULT:
                    delta.add(HEIGHT_DEFAULT, a.getInt(attr, c.layout.heightDefault));
                    break;
                case HEIGHT_MAX:
                    delta.add(HEIGHT_MAX, a.getDimensionPixelSize(attr, c.layout.heightMax));
                    break;
                case WIDTH_MAX:
                    delta.add(WIDTH_MAX, a.getDimensionPixelSize(attr, c.layout.widthMax));
                    break;
                case HEIGHT_MIN:
                    delta.add(HEIGHT_MIN, a.getDimensionPixelSize(attr, c.layout.heightMin));
                    break;
                case WIDTH_MIN:
                    delta.add(WIDTH_MIN, a.getDimensionPixelSize(attr, c.layout.widthMin));
                    break;
                case CONSTRAINED_WIDTH:
                    delta.add(CONSTRAINED_WIDTH, a.getBoolean(attr, c.layout.constrainedWidth));
                    break;
                case CONSTRAINED_HEIGHT:
                    delta.add(CONSTRAINED_HEIGHT, a.getBoolean(attr, c.layout.constrainedHeight));
                    break;
                case LAYOUT_VISIBILITY:
                    delta.add(LAYOUT_VISIBILITY,
                            VISIBILITY_FLAGS[a.getInt(attr, c.propertySet.visibility)]);
                    break;
                case VISIBILITY_MODE:
                    delta.add(VISIBILITY_MODE, a.getInt(attr, c.propertySet.mVisibilityMode));
                    break;
                case ALPHA:
                    delta.add(ALPHA, a.getFloat(attr, c.propertySet.alpha));
                    break;
                case ELEVATION:
                    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                        delta.add(ELEVATION, true);
                        delta.add(ELEVATION, a.getDimension(attr, c.transform.elevation));
                    }
                    break;
                case ROTATION:
                    delta.add(ROTATION, a.getFloat(attr, c.transform.rotation));
                    break;
                case ROTATION_X:
                    delta.add(ROTATION_X, a.getFloat(attr, c.transform.rotationX));
                    break;
                case ROTATION_Y:
                    delta.add(ROTATION_Y, a.getFloat(attr, c.transform.rotationY));
                    break;
                case SCALE_X:
                    delta.add(SCALE_X, a.getFloat(attr, c.transform.scaleX));
                    break;
                case SCALE_Y:
                    delta.add(SCALE_Y, a.getFloat(attr, c.transform.scaleY));
                    break;
                case TRANSFORM_PIVOT_X:
                    delta.add(TRANSFORM_PIVOT_X, a.getDimension(attr, c.transform.transformPivotX));
                    break;
                case TRANSFORM_PIVOT_Y:
                    delta.add(TRANSFORM_PIVOT_Y, a.getDimension(attr, c.transform.transformPivotY));
                    break;
                case TRANSLATION_X:
                    delta.add(TRANSLATION_X, a.getDimension(attr, c.transform.translationX));
                    break;
                case TRANSLATION_Y:
                    delta.add(TRANSLATION_Y, a.getDimension(attr, c.transform.translationY));
                    break;
                case TRANSLATION_Z:
                    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                        delta.add(TRANSLATION_Z, a.getDimension(attr, c.transform.translationZ));
                    }
                    break;
                case TRANSFORM_PIVOT_TARGET:
                    delta.add(TRANSFORM_PIVOT_TARGET,
                            lookupID(a, attr, c.transform.transformPivotTarget));
                    break;
                case VERTICAL_WEIGHT:
                    delta.add(VERTICAL_WEIGHT, a.getFloat(attr, c.layout.verticalWeight));
                    break;
                case HORIZONTAL_WEIGHT:
                    delta.add(HORIZONTAL_WEIGHT, a.getFloat(attr, c.layout.horizontalWeight));
                    break;
                case VERTICAL_STYLE:
                    delta.add(VERTICAL_STYLE, a.getInt(attr, c.layout.verticalChainStyle));
                    break;
                case HORIZONTAL_STYLE:
                    delta.add(HORIZONTAL_STYLE, a.getInt(attr, c.layout.horizontalChainStyle));
                    break;
                case VIEW_ID:
                    c.mViewId = a.getResourceId(attr, c.mViewId);
                    delta.add(VIEW_ID, c.mViewId);
                    break;
                case MOTION_TARGET:
                    if (MotionLayout.IS_IN_EDIT_MODE) {
                        c.mViewId = a.getResourceId(attr, c.mViewId);
                        if (c.mViewId == -1) {
                            c.mTargetString = a.getString(attr);
                        }
                    } else {
                        if (a.peekValue(attr).type == TypedValue.TYPE_STRING) {
                            c.mTargetString = a.getString(attr);
                        } else {
                            c.mViewId = a.getResourceId(attr, c.mViewId);
                        }
                    }
                    break;
                case DIMENSION_RATIO:
                    delta.add(DIMENSION_RATIO, a.getString(attr));
                    break;
                case WIDTH_PERCENT:
                    delta.add(WIDTH_PERCENT, a.getFloat(attr, 1));
                    break;
                case HEIGHT_PERCENT:
                    delta.add(HEIGHT_PERCENT, a.getFloat(attr, 1));
                    break;
                case PROGRESS:
                    delta.add(PROGRESS, a.getFloat(attr, c.propertySet.mProgress));
                    break;
                case ANIMATE_RELATIVE_TO:
                    delta.add(ANIMATE_RELATIVE_TO,
                            lookupID(a, attr, c.motion.mAnimateRelativeTo));
                    break;
                case ANIMATE_CIRCLE_ANGLE_TO:
                    delta.add(ANIMATE_CIRCLE_ANGLE_TO,
                            a.getInteger(attr, c.motion.mAnimateCircleAngleTo));
                    break;
                case TRANSITION_EASING:
                    type = a.peekValue(attr);
                    if (type.type == TypedValue.TYPE_STRING) {
                        delta.add(TRANSITION_EASING, a.getString(attr));
                    } else {
                        delta.add(TRANSITION_EASING,
                                Easing.NAMED_EASING[a.getInteger(attr, 0)]);
                    }
                    break;
                case PATH_MOTION_ARC:
                    delta.add(PATH_MOTION_ARC, a.getInt(attr, c.motion.mPathMotionArc));
                    break;
                case TRANSITION_PATH_ROTATE:
                    delta.add(TRANSITION_PATH_ROTATE, a.getFloat(attr, c.motion.mPathRotate));
                    break;
                case MOTION_STAGGER:
                    delta.add(MOTION_STAGGER, a.getFloat(attr, c.motion.mMotionStagger));
                    break;

                case QUANTIZE_MOTION_STEPS:
                    delta.add(QUANTIZE_MOTION_STEPS, a.getInteger(attr,
                            c.motion.mQuantizeMotionSteps));
                    break;
                case QUANTIZE_MOTION_PHASE:
                    delta.add(QUANTIZE_MOTION_PHASE, a.getFloat(attr,
                            c.motion.mQuantizeMotionPhase));
                    break;
                case QUANTIZE_MOTION_INTERPOLATOR:
                    type = a.peekValue(attr);
                    if (type.type == TypedValue.TYPE_REFERENCE) {
                        c.motion.mQuantizeInterpolatorID = a.getResourceId(attr, -1);
                        delta.add(QUANTIZE_MOTION_INTERPOLATOR_ID,
                                c.motion.mQuantizeInterpolatorID);
                        if (c.motion.mQuantizeInterpolatorID != -1) {
                            c.motion.mQuantizeInterpolatorType = Motion.INTERPOLATOR_REFERENCE_ID;
                            delta.add(QUANTIZE_MOTION_INTERPOLATOR_TYPE,
                                    c.motion.mQuantizeInterpolatorType);
                        }
                    } else if (type.type == TypedValue.TYPE_STRING) {
                        c.motion.mQuantizeInterpolatorString = a.getString(attr);
                        delta.add(QUANTIZE_MOTION_INTERPOLATOR_STR,
                                c.motion.mQuantizeInterpolatorString);

                        if (c.motion.mQuantizeInterpolatorString.indexOf("/") > 0) {
                            c.motion.mQuantizeInterpolatorID = a.getResourceId(attr, -1);
                            delta.add(QUANTIZE_MOTION_INTERPOLATOR_ID,
                                    c.motion.mQuantizeInterpolatorID);

                            c.motion.mQuantizeInterpolatorType = Motion.INTERPOLATOR_REFERENCE_ID;
                            delta.add(QUANTIZE_MOTION_INTERPOLATOR_TYPE,
                                    c.motion.mQuantizeInterpolatorType);

                        } else {
                            c.motion.mQuantizeInterpolatorType = Motion.SPLINE_STRING;
                            delta.add(QUANTIZE_MOTION_INTERPOLATOR_TYPE,
                                    c.motion.mQuantizeInterpolatorType);
                        }
                    } else {
                        c.motion.mQuantizeInterpolatorType =
                                a.getInteger(attr, c.motion.mQuantizeInterpolatorID);
                        delta.add(QUANTIZE_MOTION_INTERPOLATOR_TYPE,
                                c.motion.mQuantizeInterpolatorType);
                    }
                    break;
                case DRAW_PATH:
                    delta.add(DRAW_PATH, a.getInt(attr, 0));
                    break;
                case CHAIN_USE_RTL:
                    Log.e(TAG, "CURRENTLY UNSUPPORTED"); // TODO add support or remove
                    //  TODO add support or remove
                    //   c.mChainUseRtl = a.getBoolean(attr,c.mChainUseRtl);
                    break;
                case BARRIER_DIRECTION:
                    delta.add(BARRIER_DIRECTION, a.getInt(attr, c.layout.mBarrierDirection));
                    break;
                case BARRIER_MARGIN:
                    delta.add(BARRIER_MARGIN, a.getDimensionPixelSize(attr,
                            c.layout.mBarrierMargin));
                    break;
                case CONSTRAINT_REFERENCED_IDS:
                    delta.add(CONSTRAINT_REFERENCED_IDS, a.getString(attr));
                    break;
                case CONSTRAINT_TAG:
                    delta.add(CONSTRAINT_TAG, a.getString(attr));
                    break;
                case BARRIER_ALLOWS_GONE_WIDGETS:
                    delta.add(BARRIER_ALLOWS_GONE_WIDGETS, a.getBoolean(attr,
                            c.layout.mBarrierAllowsGoneWidgets));
                    break;
                case UNUSED:
                    Log.w(TAG,
                            "unused attribute 0x" + Integer.toHexString(attr)
                                    + "   " + sMapToConstant.get(attr));
                    break;
                default:
                    Log.w(TAG,
                            "Unknown attribute 0x" + Integer.toHexString(attr)
                                    + "   " + sMapToConstant.get(attr));
            }
        }
    }

    private static void setDeltaValue(Constraint c, int type, float value) {
        switch (type) {
            case GUIDE_PERCENT:
                c.layout.guidePercent = value;
                break;
            case CIRCLE_ANGLE:
                c.layout.circleAngle = value;
                break;
            case HORIZONTAL_BIAS:
                c.layout.horizontalBias = value;
                break;
            case VERTICAL_BIAS:
                c.layout.verticalBias = value;
                break;
            case ALPHA:
                c.propertySet.alpha = value;
                break;
            case ELEVATION:
                c.transform.elevation = value;
                c.transform.applyElevation = true;
                break;
            case ROTATION:
                c.transform.rotation = value;
                break;
            case ROTATION_X:
                c.transform.rotationX = value;
                break;
            case ROTATION_Y:
                c.transform.rotationY = value;
                break;
            case SCALE_X:
                c.transform.scaleX = value;
                break;
            case SCALE_Y:
                c.transform.scaleY = value;
                break;
            case TRANSFORM_PIVOT_X:
                c.transform.transformPivotX = value;
                break;
            case TRANSFORM_PIVOT_Y:
                c.transform.transformPivotY = value;
                break;
            case TRANSLATION_X:
                c.transform.translationX = value;
                break;
            case TRANSLATION_Y:
                c.transform.translationY = value;
                break;
            case TRANSLATION_Z:
                c.transform.translationZ = value;
                break;
            case VERTICAL_WEIGHT:
                c.layout.verticalWeight = value;
                break;
            case HORIZONTAL_WEIGHT:
                c.layout.horizontalWeight = value;
                break;
            case WIDTH_PERCENT:
                c.layout.widthPercent = value;
                break;
            case HEIGHT_PERCENT:
                c.layout.heightPercent = value;
                break;
            case PROGRESS:
                c.propertySet.mProgress = value;
                break;
            case TRANSITION_PATH_ROTATE:
                c.motion.mPathRotate = value;
                break;
            case MOTION_STAGGER:
                c.motion.mMotionStagger = value;
                break;
            case QUANTIZE_MOTION_PHASE:
                c.motion.mQuantizeMotionPhase = value;
                break;
            case UNUSED:
                break;
            default:
                Log.w(TAG,
                        "Unknown attribute 0x");
        }
    }

    private static void setDeltaValue(Constraint c, int type, int value) {
        switch (type) {
            case EDITOR_ABSOLUTE_X:
                c.layout.editorAbsoluteX = value;
                break;
            case EDITOR_ABSOLUTE_Y:
                c.layout.editorAbsoluteY = value;
                break;
            case LAYOUT_WRAP_BEHAVIOR:
                c.layout.mWrapBehavior = value;
                break;
            case GUIDE_BEGIN:
                c.layout.guideBegin = value;
                break;
            case GUIDE_END:
                c.layout.guideEnd = value;
                break;
            case ORIENTATION:
                c.layout.orientation = value;
                break;
            case CIRCLE:
                c.layout.circleConstraint = value;
                break;
            case CIRCLE_RADIUS:
                c.layout.circleRadius = value;
                break;
            case GONE_LEFT_MARGIN:
                c.layout.goneLeftMargin = value;
                break;
            case GONE_TOP_MARGIN:
                c.layout.goneTopMargin = value;
                break;
            case GONE_RIGHT_MARGIN:
                c.layout.goneRightMargin = value;
                break;
            case GONE_BOTTOM_MARGIN:
                c.layout.goneBottomMargin = value;
                break;
            case GONE_START_MARGIN:
                c.layout.goneStartMargin = value;
                break;
            case GONE_END_MARGIN:
                c.layout.goneEndMargin = value;
                break;
            case GONE_BASELINE_MARGIN:
                c.layout.goneBaselineMargin = value;
                break;
            case LEFT_MARGIN:
                c.layout.leftMargin = value;
                break;
            case RIGHT_MARGIN:
                c.layout.rightMargin = value;
                break;
            case START_MARGIN:
                c.layout.startMargin = value;
                break;
            case END_MARGIN:
                c.layout.endMargin = value;
                break;
            case TOP_MARGIN:
                c.layout.topMargin = value;
                break;
            case BOTTOM_MARGIN:
                c.layout.bottomMargin = value;
                break;
            case BASELINE_MARGIN:
                c.layout.baselineMargin = value;
                break;
            case LAYOUT_WIDTH:
                c.layout.mWidth = value;
                break;
            case LAYOUT_HEIGHT:
                c.layout.mHeight = value;
                break;
            case WIDTH_DEFAULT:
                c.layout.widthDefault = value;
                break;
            case HEIGHT_DEFAULT:
                c.layout.heightDefault = value;
                break;
            case HEIGHT_MAX:
                c.layout.heightMax = value;
                break;
            case WIDTH_MAX:
                c.layout.widthMax = value;
                break;
            case HEIGHT_MIN:
                c.layout.heightMin = value;
                break;
            case WIDTH_MIN:
                c.layout.widthMin = value;
                break;
            case LAYOUT_VISIBILITY:
                c.propertySet.visibility = value;
                break;
            case VISIBILITY_MODE:
                c.propertySet.mVisibilityMode = value;
                break;
            case TRANSFORM_PIVOT_TARGET:
                c.transform.transformPivotTarget = value;
                break;
            case VERTICAL_STYLE:
                c.layout.verticalChainStyle = value;
                break;
            case HORIZONTAL_STYLE:
                c.layout.horizontalChainStyle = value;
                break;
            case VIEW_ID:
                c.mViewId = value;
                break;
            case ANIMATE_RELATIVE_TO:
                c.motion.mAnimateRelativeTo = value;
                break;
            case ANIMATE_CIRCLE_ANGLE_TO:
                c.motion.mAnimateCircleAngleTo = value;
                break;
            case PATH_MOTION_ARC:
                c.motion.mPathMotionArc = value;
                break;
            case QUANTIZE_MOTION_STEPS:
                c.motion.mQuantizeMotionSteps = value;
                break;
            case QUANTIZE_MOTION_INTERPOLATOR_TYPE:
                c.motion.mQuantizeInterpolatorType = value;
                break;
            case QUANTIZE_MOTION_INTERPOLATOR_ID:
                c.motion.mQuantizeInterpolatorID = value;
                break;
            case DRAW_PATH:
                c.motion.mDrawPath = value;
                break;
            case BARRIER_DIRECTION:
                c.layout.mBarrierDirection = value;
                break;
            case BARRIER_MARGIN:
                c.layout.mBarrierMargin = value;
                break;
            case UNUSED:
                break;
            default:
                Log.w(TAG,
                        "Unknown attribute 0x");
        }
    }

    private static void setDeltaValue(Constraint c, int type, String value) {
        switch (type) {
            case DIMENSION_RATIO:
                c.layout.dimensionRatio = value;
                break;
            case TRANSITION_EASING:
                c.motion.mTransitionEasing = value;
                break;
            case QUANTIZE_MOTION_INTERPOLATOR_STR:
                c.motion.mQuantizeInterpolatorString = value;
                break;
            case CONSTRAINT_REFERENCED_IDS:
                c.layout.mReferenceIdString = value;
                // If a string is defined, clear up the reference ids array
                c.layout.mReferenceIds = null;
                break;
            case CONSTRAINT_TAG:
                c.layout.mConstraintTag = value;
                break;
            case UNUSED:
                break;
            default:
                Log.w(TAG,
                        "Unknown attribute 0x");
        }
    }

    private static void setDeltaValue(Constraint c, int type, boolean value) {
        switch (type) {
            case CONSTRAINED_WIDTH:
                c.layout.constrainedWidth = value;
                break;
            case CONSTRAINED_HEIGHT:
                c.layout.constrainedHeight = value;
                break;
            case ELEVATION:
                c.transform.applyElevation = value;
                break;
            case BARRIER_ALLOWS_GONE_WIDGETS:
                c.layout.mBarrierAllowsGoneWidgets = value;
                break;
            case UNUSED:
                break;
            default:
                Log.w(TAG, "Unknown attribute 0x");
        }
    }

    private void populateConstraint(Constraint c, TypedArray a, boolean override) {
        if (override) {
            populateOverride(c, a);
            return;
        }
        final int count = a.getIndexCount();
        for (int i = 0; i < count; i++) {
            int attr = a.getIndex(i);
            if (DEBUG) { // USEFUL when adding features to track tags being parsed
                try {
                    Field[] campos = R.styleable.class.getFields();
                    boolean found = false;
                    for (Field f : campos) {
                        try {
                            if (f.getType().isPrimitive()
                                    && attr == f.getInt(null) && f.getName()
                                    .contains("Constraint_")) {
                                found = true;
                                if (DEBUG) {
                                    Log.v(TAG, "L id " + f.getName() + " #" + attr);
                                }
                                break;
                            }
                        } catch (Exception e) {

                        }
                    }
                    if (!found) {
                        campos = android.R.attr.class.getFields();
                        for (Field f : campos) {
                            try {
                                if (f.getType().isPrimitive() && attr == f.getInt(null)
                                        && f.getName()
                                        .contains("Constraint_")) {
                                    found = false;
                                    if (DEBUG) {
                                        Log.v(TAG, "x id " + f.getName());
                                    }
                                    break;
                                }
                            } catch (Exception e) {

                            }
                        }
                    }
                    if (!found) {
                        Log.v(TAG, " ? " + attr);
                    }
                } catch (Exception e) {
                    Log.v(TAG, " " + e.toString());
                }
            }


            if (attr != R.styleable.Constraint_android_id
                    && R.styleable.Constraint_android_layout_marginStart != attr
                    && R.styleable.Constraint_android_layout_marginEnd != attr) {
                c.motion.mApply = true;
                c.layout.mApply = true;
                c.propertySet.mApply = true;
                c.transform.mApply = true;
            }

            switch (sMapToConstant.get(attr)) {
                case LEFT_TO_LEFT:
                    c.layout.leftToLeft = lookupID(a, attr, c.layout.leftToLeft);
                    break;
                case LEFT_TO_RIGHT:
                    c.layout.leftToRight = lookupID(a, attr, c.layout.leftToRight);
                    break;
                case RIGHT_TO_LEFT:
                    c.layout.rightToLeft = lookupID(a, attr, c.layout.rightToLeft);
                    break;
                case RIGHT_TO_RIGHT:
                    c.layout.rightToRight = lookupID(a, attr, c.layout.rightToRight);
                    break;
                case TOP_TO_TOP:
                    c.layout.topToTop = lookupID(a, attr, c.layout.topToTop);
                    break;
                case TOP_TO_BOTTOM:
                    c.layout.topToBottom = lookupID(a, attr, c.layout.topToBottom);
                    break;
                case BOTTOM_TO_TOP:
                    c.layout.bottomToTop = lookupID(a, attr, c.layout.bottomToTop);
                    break;
                case BOTTOM_TO_BOTTOM:
                    c.layout.bottomToBottom = lookupID(a, attr, c.layout.bottomToBottom);
                    break;
                case BASELINE_TO_BASELINE:
                    c.layout.baselineToBaseline = lookupID(a, attr, c.layout.baselineToBaseline);
                    break;
                case BASELINE_TO_TOP:
                    c.layout.baselineToTop = lookupID(a, attr, c.layout.baselineToTop);
                    break;
                case BASELINE_TO_BOTTOM:
                    c.layout.baselineToBottom = lookupID(a, attr, c.layout.baselineToBottom);
                    break;
                case EDITOR_ABSOLUTE_X:
                    c.layout.editorAbsoluteX = a.getDimensionPixelOffset(attr,
                            c.layout.editorAbsoluteX);
                    break;
                case EDITOR_ABSOLUTE_Y:
                    c.layout.editorAbsoluteY = a.getDimensionPixelOffset(attr,
                            c.layout.editorAbsoluteY);
                    break;
                case GUIDE_BEGIN:
                    c.layout.guideBegin = a.getDimensionPixelOffset(attr, c.layout.guideBegin);
                    break;
                case GUIDE_END:
                    c.layout.guideEnd = a.getDimensionPixelOffset(attr, c.layout.guideEnd);
                    break;
                case GUIDE_PERCENT:
                    c.layout.guidePercent = a.getFloat(attr, c.layout.guidePercent);
                    break;
                case ORIENTATION:
                    c.layout.orientation = a.getInt(attr, c.layout.orientation);
                    break;
                case START_TO_END:
                    c.layout.startToEnd = lookupID(a, attr, c.layout.startToEnd);
                    break;
                case START_TO_START:
                    c.layout.startToStart = lookupID(a, attr, c.layout.startToStart);
                    break;
                case END_TO_START:
                    c.layout.endToStart = lookupID(a, attr, c.layout.endToStart);
                    break;
                case END_TO_END:
                    c.layout.endToEnd = lookupID(a, attr, c.layout.endToEnd);
                    break;
                case CIRCLE:
                    c.layout.circleConstraint = lookupID(a, attr, c.layout.circleConstraint);
                    break;
                case CIRCLE_RADIUS:
                    c.layout.circleRadius = a.getDimensionPixelSize(attr, c.layout.circleRadius);
                    break;
                case CIRCLE_ANGLE:
                    c.layout.circleAngle = a.getFloat(attr, c.layout.circleAngle);
                    break;
                case GONE_LEFT_MARGIN:
                    c.layout.goneLeftMargin = a.getDimensionPixelSize(attr,
                            c.layout.goneLeftMargin);
                    break;
                case GONE_TOP_MARGIN:
                    c.layout.goneTopMargin = a.getDimensionPixelSize(attr, c.layout.goneTopMargin);
                    break;
                case GONE_RIGHT_MARGIN:
                    c.layout.goneRightMargin = a.getDimensionPixelSize(attr,
                            c.layout.goneRightMargin);
                    break;
                case GONE_BOTTOM_MARGIN:
                    c.layout.goneBottomMargin = a.getDimensionPixelSize(attr,
                            c.layout.goneBottomMargin);
                    break;
                case GONE_START_MARGIN:
                    c.layout.goneStartMargin =
                            a.getDimensionPixelSize(attr, c.layout.goneStartMargin);
                    break;
                case GONE_END_MARGIN:
                    c.layout.goneEndMargin = a.getDimensionPixelSize(attr, c.layout.goneEndMargin);
                    break;
                case GONE_BASELINE_MARGIN:
                    c.layout.goneBaselineMargin = a.getDimensionPixelSize(attr,
                            c.layout.goneBaselineMargin);
                    break;
                case HORIZONTAL_BIAS:
                    c.layout.horizontalBias = a.getFloat(attr, c.layout.horizontalBias);
                    break;
                case VERTICAL_BIAS:
                    c.layout.verticalBias = a.getFloat(attr, c.layout.verticalBias);
                    break;
                case LEFT_MARGIN:
                    c.layout.leftMargin = a.getDimensionPixelSize(attr, c.layout.leftMargin);
                    break;
                case RIGHT_MARGIN:
                    c.layout.rightMargin = a.getDimensionPixelSize(attr, c.layout.rightMargin);
                    break;
                case START_MARGIN:
                    c.layout.startMargin = a.getDimensionPixelSize(attr, c.layout.startMargin);
                    break;
                case END_MARGIN:
                    c.layout.endMargin = a.getDimensionPixelSize(attr, c.layout.endMargin);
                    break;
                case TOP_MARGIN:
                    c.layout.topMargin = a.getDimensionPixelSize(attr, c.layout.topMargin);
                    break;
                case BOTTOM_MARGIN:
                    c.layout.bottomMargin = a.getDimensionPixelSize(attr, c.layout.bottomMargin);
                    break;
                case BASELINE_MARGIN:
                    c.layout.baselineMargin = a.getDimensionPixelSize(attr,
                            c.layout.baselineMargin);
                    break;
                case LAYOUT_WIDTH:
                    c.layout.mWidth = a.getLayoutDimension(attr, c.layout.mWidth);
                    break;
                case LAYOUT_HEIGHT:
                    c.layout.mHeight = a.getLayoutDimension(attr, c.layout.mHeight);
                    break;
                case LAYOUT_CONSTRAINT_WIDTH:
                    ConstraintSet.parseDimensionConstraints(c.layout, a, attr, HORIZONTAL);
                    break;
                case LAYOUT_CONSTRAINT_HEIGHT:
                    ConstraintSet.parseDimensionConstraints(c.layout, a, attr, VERTICAL);
                    break;
                case LAYOUT_WRAP_BEHAVIOR:
                    c.layout.mWrapBehavior = a.getInt(attr, c.layout.mWrapBehavior);
                    break;
                case WIDTH_DEFAULT:
                    c.layout.widthDefault = a.getInt(attr, c.layout.widthDefault);
                    break;
                case HEIGHT_DEFAULT:
                    c.layout.heightDefault = a.getInt(attr, c.layout.heightDefault);
                    break;
                case HEIGHT_MAX:
                    c.layout.heightMax = a.getDimensionPixelSize(attr, c.layout.heightMax);
                    break;
                case WIDTH_MAX:
                    c.layout.widthMax = a.getDimensionPixelSize(attr, c.layout.widthMax);
                    break;
                case HEIGHT_MIN:
                    c.layout.heightMin = a.getDimensionPixelSize(attr, c.layout.heightMin);
                    break;
                case WIDTH_MIN:
                    c.layout.widthMin = a.getDimensionPixelSize(attr, c.layout.widthMin);
                    break;
                case CONSTRAINED_WIDTH:
                    c.layout.constrainedWidth = a.getBoolean(attr, c.layout.constrainedWidth);
                    break;
                case CONSTRAINED_HEIGHT:
                    c.layout.constrainedHeight = a.getBoolean(attr, c.layout.constrainedHeight);
                    break;
                case LAYOUT_VISIBILITY:
                    c.propertySet.visibility = a.getInt(attr, c.propertySet.visibility);
                    c.propertySet.visibility = VISIBILITY_FLAGS[c.propertySet.visibility];
                    break;
                case VISIBILITY_MODE:
                    c.propertySet.mVisibilityMode = a.getInt(attr, c.propertySet.mVisibilityMode);
                    break;
                case ALPHA:
                    c.propertySet.alpha = a.getFloat(attr, c.propertySet.alpha);
                    break;
                case ELEVATION:
                    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                        c.transform.applyElevation = true;
                        c.transform.elevation = a.getDimension(attr, c.transform.elevation);
                    }
                    break;
                case ROTATION:
                    c.transform.rotation = a.getFloat(attr, c.transform.rotation);
                    break;
                case ROTATION_X:
                    c.transform.rotationX = a.getFloat(attr, c.transform.rotationX);
                    break;
                case ROTATION_Y:
                    c.transform.rotationY = a.getFloat(attr, c.transform.rotationY);
                    break;
                case SCALE_X:
                    c.transform.scaleX = a.getFloat(attr, c.transform.scaleX);
                    break;
                case SCALE_Y:
                    c.transform.scaleY = a.getFloat(attr, c.transform.scaleY);
                    break;
                case TRANSFORM_PIVOT_X:
                    c.transform.transformPivotX = a.getDimension(attr, c.transform.transformPivotX);
                    break;
                case TRANSFORM_PIVOT_Y:
                    c.transform.transformPivotY = a.getDimension(attr, c.transform.transformPivotY);
                    break;
                case TRANSLATION_X:
                    c.transform.translationX = a.getDimension(attr, c.transform.translationX);
                    break;
                case TRANSLATION_Y:
                    c.transform.translationY = a.getDimension(attr, c.transform.translationY);
                    break;
                case TRANSLATION_Z:
                    if (Build.VERSION.SDK_INT >= VERSION_CODES.LOLLIPOP) {
                        c.transform.translationZ = a.getDimension(attr, c.transform.translationZ);
                    }
                    break;
                case TRANSFORM_PIVOT_TARGET:
                    c.transform.transformPivotTarget =
                            lookupID(a, attr, c.transform.transformPivotTarget);
                    break;
                case VERTICAL_WEIGHT:
                    c.layout.verticalWeight = a.getFloat(attr, c.layout.verticalWeight);
                    break;
                case HORIZONTAL_WEIGHT:
                    c.layout.horizontalWeight = a.getFloat(attr, c.layout.horizontalWeight);
                    break;
                case VERTICAL_STYLE:
                    c.layout.verticalChainStyle = a.getInt(attr, c.layout.verticalChainStyle);
                    break;
                case HORIZONTAL_STYLE:
                    c.layout.horizontalChainStyle = a.getInt(attr, c.layout.horizontalChainStyle);
                    break;
                case VIEW_ID:
                    c.mViewId = a.getResourceId(attr, c.mViewId);
                    break;
                case DIMENSION_RATIO:
                    c.layout.dimensionRatio = a.getString(attr);
                    break;
                case WIDTH_PERCENT:
                    c.layout.widthPercent = a.getFloat(attr, 1);
                    break;
                case HEIGHT_PERCENT:
                    c.layout.heightPercent = a.getFloat(attr, 1);
                    break;
                case PROGRESS:
                    c.propertySet.mProgress = a.getFloat(attr, c.propertySet.mProgress);
                    break;
                case ANIMATE_RELATIVE_TO:
                    c.motion.mAnimateRelativeTo = lookupID(a, attr, c.motion.mAnimateRelativeTo);
                    break;
                case ANIMATE_CIRCLE_ANGLE_TO:
                    c.motion.mAnimateCircleAngleTo =
                            a.getInteger(attr, c.motion.mAnimateCircleAngleTo);
                    break;
                case TRANSITION_EASING:
                    TypedValue type = a.peekValue(attr);
                    if (type.type == TypedValue.TYPE_STRING) {
                        c.motion.mTransitionEasing = a.getString(attr);
                    } else {
                        c.motion.mTransitionEasing =
                                Easing.NAMED_EASING[a.getInteger(attr, 0)];
                    }
                    break;
                case PATH_MOTION_ARC:
                    c.motion.mPathMotionArc = a.getInt(attr, c.motion.mPathMotionArc);
                    break;
                case TRANSITION_PATH_ROTATE:
                    c.motion.mPathRotate = a.getFloat(attr, c.motion.mPathRotate);
                    break;
                case MOTION_STAGGER:
                    c.motion.mMotionStagger = a.getFloat(attr, c.motion.mMotionStagger);
                    break;

                case QUANTIZE_MOTION_STEPS:
                    c.motion.mQuantizeMotionSteps = a.getInteger(attr,
                            c.motion.mQuantizeMotionSteps);
                    break;
                case QUANTIZE_MOTION_PHASE:
                    c.motion.mQuantizeMotionPhase = a.getFloat(attr, c.motion.mQuantizeMotionPhase);
                    break;
                case QUANTIZE_MOTION_INTERPOLATOR:
                    type = a.peekValue(attr);

                    if (type.type == TypedValue.TYPE_REFERENCE) {
                        c.motion.mQuantizeInterpolatorID = a.getResourceId(attr, -1);
                        if (c.motion.mQuantizeInterpolatorID != -1) {
                            c.motion.mQuantizeInterpolatorType = Motion.INTERPOLATOR_REFERENCE_ID;
                        }
                    } else if (type.type == TypedValue.TYPE_STRING) {
                        c.motion.mQuantizeInterpolatorString = a.getString(attr);
                        if (c.motion.mQuantizeInterpolatorString.indexOf("/") > 0) {
                            c.motion.mQuantizeInterpolatorID = a.getResourceId(attr, -1);
                            c.motion.mQuantizeInterpolatorType = Motion.INTERPOLATOR_REFERENCE_ID;
                        } else {
                            c.motion.mQuantizeInterpolatorType = Motion.SPLINE_STRING;
                        }
                    } else {
                        c.motion.mQuantizeInterpolatorType = a.getInteger(attr,
                                c.motion.mQuantizeInterpolatorID);
                    }

                    break;


                case DRAW_PATH:
                    c.motion.mDrawPath = a.getInt(attr, 0);
                    break;
                case CHAIN_USE_RTL:
                    Log.e(TAG, "CURRENTLY UNSUPPORTED"); // TODO add support or remove
                    //  TODO add support or remove  c.mChainUseRtl =
                    //   a.getBoolean(attr,c.mChainUseRtl);
                    break;
                case BARRIER_DIRECTION:
                    c.layout.mBarrierDirection = a.getInt(attr, c.layout.mBarrierDirection);
                    break;
                case BARRIER_MARGIN:
                    c.layout.mBarrierMargin = a.getDimensionPixelSize(attr,
                            c.layout.mBarrierMargin);
                    break;
                case CONSTRAINT_REFERENCED_IDS:
                    c.layout.mReferenceIdString = a.getString(attr);
                    break;
                case CONSTRAINT_TAG:
                    c.layout.mConstraintTag = a.getString(attr);
                    break;
                case BARRIER_ALLOWS_GONE_WIDGETS:
                    c.layout.mBarrierAllowsGoneWidgets = a.getBoolean(attr,
                            c.layout.mBarrierAllowsGoneWidgets);
                    break;
                case UNUSED:
                    Log.w(TAG,
                            "unused attribute 0x" + Integer.toHexString(attr)
                                    + "   " + sMapToConstant.get(attr));
                    break;
                default:
                    Log.w(TAG,
                            "Unknown attribute 0x" + Integer.toHexString(attr)
                                    + "   " + sMapToConstant.get(attr));
            }
        }
        if (c.layout.mReferenceIdString != null) {
            // in case the strings are set, make sure to clear up the cached ids
            c.layout.mReferenceIds = null;
        }
    }

    private int[] convertReferenceString(View view, String referenceIdString) {
        String[] split = referenceIdString.split(",");
        Context context = view.getContext();
        int[] tags = new int[split.length];
        int count = 0;
        for (int i = 0; i < split.length; i++) {
            String idString = split[i];
            idString = idString.trim();
            int tag = 0;
            try {
                Class res = R.id.class;
                Field field = res.getField(idString);
                tag = field.getInt(null);
            } catch (Exception e) {
                // Do nothing
            }
            if (tag == 0) {
                tag = context.getResources().getIdentifier(idString, "id",
                        context.getPackageName());
            }

            if (tag == 0 && view.isInEditMode() && view.getParent() instanceof ConstraintLayout) {
                ConstraintLayout constraintLayout = (ConstraintLayout) view.getParent();
                Object value = constraintLayout.getDesignInformation(0, idString);
                if (value != null && value instanceof Integer) {
                    tag = (Integer) value;
                }
            }
            tags[count++] = tag;
        }
        if (count != split.length) {
            tags = Arrays.copyOf(tags, count);
        }
        return tags;
    }

    /**
     *
     */
    public Constraint getConstraint(int id) {
        if (mConstraints.containsKey(id)) {
            return mConstraints.get(id);
        }
        return null;
    }

    /**
     *
     */
    public int[] getKnownIds() {
        Integer[] arr = mConstraints.keySet().toArray(new Integer[0]);
        int[] array = new int[arr.length];
        for (int i = 0; i < array.length; i++) {
            array[i] = arr[i];
        }
        return array;
    }

    /**
     * Enforce id are required for all ConstraintLayout children to use ConstraintSet.
     * default = true;
     */
    public boolean isForceId() {
        return mForceId;
    }

    /**
     * Enforce id are required for all ConstraintLayout children to use ConstraintSet.
     * default = true;
     *
     * @param forceId
     */
    public void setForceId(boolean forceId) {
        this.mForceId = forceId;
    }

    /**
     * If true perform validation checks when parsing ConstraintSets
     * This will slow down parsing and should only be used for debugging
     *
     * @param validate
     */
    public void setValidateOnParse(boolean validate) {
        mValidate = validate;
    }

    /**
     * If true perform validation checks when parsing ConstraintSets
     * This will slow down parsing and should only be used for debugging
     *
     * @return validate
     */
    public boolean isValidateOnParse() {
        return mValidate;
    }

    /**
     * Dump the contents
     *
     * @param scene
     * @param ids
     */
    public void dump(MotionScene scene, int... ids) {
        Set<Integer> keys = mConstraints.keySet();
        HashSet<Integer> set;
        if (ids.length != 0) {
            set = new HashSet<Integer>();
            for (int id : ids) {
                set.add(id);
            }
        } else {
            set = new HashSet<>(keys);
        }
        System.out.println(set.size() + " constraints");
        StringBuilder stringBuilder = new StringBuilder();

        for (Integer id : set.toArray(new Integer[0])) {
            Constraint constraint = mConstraints.get(id);
            if (constraint == null) {
                continue;
            }

            stringBuilder.append("<Constraint id=");
            stringBuilder.append(id);
            stringBuilder.append(" \n");
            constraint.layout.dump(scene, stringBuilder);
            stringBuilder.append("/>\n");
        }
        System.out.println(stringBuilder.toString());

    }

    /**
     * Construct a user friendly error string
     *
     * @param context    the context
     * @param resourceId the xml being parsed
     * @param pullParser the XML parser
     * @return
     */
    static String getLine(Context context, int resourceId, XmlPullParser pullParser) {
        return ".(" + Debug.getName(context, resourceId)
                + ".xml:" + pullParser.getLineNumber()
                + ") \"" + pullParser.getName() + "\"";
    }

    static String getDebugName(int v) {
        for (Field field : ConstraintSet.class.getDeclaredFields()) {
            if (field.getName().contains("_")
                    && field.getType() == int.class
                    && java.lang.reflect.Modifier.isStatic(field.getModifiers())
                    && java.lang.reflect.Modifier.isFinal(field.getModifiers())) {
                int val = 0;
                try {
                    val = field.getInt(null);
                    if (val == v) {
                        return field.getName();
                    }
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "Error accessing ConstraintSet field", e);
                }

            }
        }
        return "UNKNOWN";
    }

    /**
     * Write the state to a Writer
     * @param writer
     * @param layout
     * @param flags
     * @throws IOException
     */
    public void writeState(Writer writer, ConstraintLayout layout, int flags) throws IOException {
        writer.write("\n---------------------------------------------\n");
        if ((flags & 1) == 1) {
            new WriteXmlEngine(writer, layout, flags).writeLayout();
        } else {
            new WriteJsonEngine(writer, layout, flags).writeLayout();
        }
        writer.write("\n---------------------------------------------\n");

    }

    class WriteXmlEngine {
        Writer mWriter;
        ConstraintLayout mLayout;
        Context mContext;
        int mFlags;
        int mUnknownCount = 0;
        final String mLEFT = "'left'";
        final String mRIGHT = "'right'";
        final String mBASELINE = "'baseline'";
        final String mBOTTOM = "'bottom'";
        final String mTOP = "'top'";
        final String mSTART = "'start'";
        final String mEND = "'end'";

        WriteXmlEngine(Writer writer, ConstraintLayout layout, int flags) throws IOException {
            this.mWriter = writer;
            this.mLayout = layout;
            this.mContext = layout.getContext();
            this.mFlags = flags;
        }

        void writeLayout() throws IOException {
            mWriter.write("\n<ConstraintSet>\n");
            for (Integer id : mConstraints.keySet()) {
                Constraint c = mConstraints.get(id);
                String idName = getName(id);
                mWriter.write("  <Constraint");
                mWriter.write(SPACE + "android:id" + "=\"" + idName + "\"");
                Layout l = c.layout;
                writeBaseDimension("android:layout_width", l.mWidth, -5);
                writeBaseDimension("android:layout_height", l.mHeight, -5);

                writeVariable("app:layout_constraintGuide_begin", l.guideBegin, UNSET);
                writeVariable("app:layout_constraintGuide_end", l.guideEnd, UNSET);
                writeVariable("app:layout_constraintGuide_percent", l.guidePercent, UNSET);

                writeVariable("app:layout_constraintHorizontal_bias",
                        l.horizontalBias, 0.5f);
                writeVariable("app:layout_constraintVertical_bias",
                        l.verticalBias, 0.5f);
                writeVariable("app:layout_constraintDimensionRatio",
                        l.dimensionRatio, null);
                writeXmlConstraint("app:layout_constraintCircle", l.circleConstraint);
                writeVariable("app:layout_constraintCircleRadius", l.circleRadius, 0);
                writeVariable("app:layout_constraintCircleAngle", l.circleAngle, 0);

                writeVariable("android:orientation", l.orientation, UNSET);

                writeVariable("app:layout_constraintVertical_weight",
                        l.verticalWeight, UNSET);
                writeVariable("app:layout_constraintHorizontal_weight",
                        l.horizontalWeight, UNSET);
                writeVariable("app:layout_constraintHorizontal_chainStyle",
                        l.horizontalChainStyle, CHAIN_SPREAD);
                writeVariable("app:layout_constraintVertical_chainStyle",
                        l.verticalChainStyle, CHAIN_SPREAD);

                writeVariable("app:barrierDirection", l.mBarrierDirection, UNSET);
                writeVariable("app:barrierMargin", l.mBarrierMargin, 0);

                writeDimension("app:layout_marginLeft", l.leftMargin, 0);
                writeDimension("app:layout_goneMarginLeft",
                        l.goneLeftMargin, Layout.UNSET_GONE_MARGIN);
                writeDimension("app:layout_marginRight", l.rightMargin, 0);
                writeDimension("app:layout_goneMarginRight",
                        l.goneRightMargin, Layout.UNSET_GONE_MARGIN);
                writeDimension("app:layout_marginStart", l.startMargin, 0);
                writeDimension("app:layout_goneMarginStart",
                        l.goneStartMargin, Layout.UNSET_GONE_MARGIN);
                writeDimension("app:layout_marginEnd", l.endMargin, 0);
                writeDimension("app:layout_goneMarginEnd",
                        l.goneEndMargin, Layout.UNSET_GONE_MARGIN);
                writeDimension("app:layout_marginTop", l.topMargin, 0);
                writeDimension("app:layout_goneMarginTop",
                        l.goneTopMargin, Layout.UNSET_GONE_MARGIN);
                writeDimension("app:layout_marginBottom", l.bottomMargin, 0);
                writeDimension("app:layout_goneMarginBottom",
                        l.goneBottomMargin, Layout.UNSET_GONE_MARGIN);
                writeDimension("app:goneBaselineMargin",
                        l.goneBaselineMargin, Layout.UNSET_GONE_MARGIN);
                writeDimension("app:baselineMargin", l.baselineMargin, 0);

                writeBoolen("app:layout_constrainedWidth", l.constrainedWidth, false);
                writeBoolen("app:layout_constrainedHeight",
                        l.constrainedHeight, false);
                writeBoolen("app:barrierAllowsGoneWidgets",
                        l.mBarrierAllowsGoneWidgets, true);
                writeVariable("app:layout_wrapBehaviorInParent", l.mWrapBehavior,
                        ConstraintWidget.WRAP_BEHAVIOR_INCLUDED);

                writeXmlConstraint("app:baselineToBaseline", l.baselineToBaseline);
                writeXmlConstraint("app:baselineToBottom", l.baselineToBottom);
                writeXmlConstraint("app:baselineToTop", l.baselineToTop);
                writeXmlConstraint("app:layout_constraintBottom_toBottomOf", l.bottomToBottom);
                writeXmlConstraint("app:layout_constraintBottom_toTopOf", l.bottomToTop);
                writeXmlConstraint("app:layout_constraintEnd_toEndOf", l.endToEnd);
                writeXmlConstraint("app:layout_constraintEnd_toStartOf", l.endToStart);
                writeXmlConstraint("app:layout_constraintLeft_toLeftOf", l.leftToLeft);
                writeXmlConstraint("app:layout_constraintLeft_toRightOf", l.leftToRight);
                writeXmlConstraint("app:layout_constraintRight_toLeftOf", l.rightToLeft);
                writeXmlConstraint("app:layout_constraintRight_toRightOf", l.rightToRight);
                writeXmlConstraint("app:layout_constraintStart_toEndOf", l.startToEnd);
                writeXmlConstraint("app:layout_constraintStart_toStartOf", l.startToStart);
                writeXmlConstraint("app:layout_constraintTop_toBottomOf", l.topToBottom);
                writeXmlConstraint("app:layout_constraintTop_toTopOf", l.topToTop);

                String[] typesConstraintDefault = {"spread", "wrap", "percent"};
                writeEnum("app:layout_constraintHeight_default", l.heightDefault,
                        typesConstraintDefault, ConstraintWidget.MATCH_CONSTRAINT_SPREAD);
                writeVariable("app:layout_constraintHeight_percent", l.heightPercent, 1);
                writeDimension("app:layout_constraintHeight_min", l.heightMin, 0);
                writeDimension("app:layout_constraintHeight_max", l.heightMax, 0);
                writeBoolen("android:layout_constrainedHeight",
                        l.constrainedHeight, false);

                writeEnum("app:layout_constraintWidth_default",
                        l.widthDefault, typesConstraintDefault,
                        ConstraintWidget.MATCH_CONSTRAINT_SPREAD);
                writeVariable("app:layout_constraintWidth_percent", l.widthPercent, 1);
                writeDimension("app:layout_constraintWidth_min", l.widthMin, 0);
                writeDimension("app:layout_constraintWidth_max", l.widthMax, 0);
                writeBoolen("android:layout_constrainedWidth",
                        l.constrainedWidth, false);

                writeVariable("app:layout_constraintVertical_weight",
                        l.verticalWeight, UNSET);
                writeVariable("app:layout_constraintHorizontal_weight",
                        l.horizontalWeight, UNSET);
                writeVariable("app:layout_constraintHorizontal_chainStyle",
                        l.horizontalChainStyle);
                writeVariable("app:layout_constraintVertical_chainStyle",
                        l.verticalChainStyle);
                String[] barrierDir = {"left", "right", "top", "bottom", "start", "end"};
                writeEnum("app:barrierDirection", l.mBarrierDirection, barrierDir, UNSET);
                writeVariable("app:layout_constraintTag", l.mConstraintTag, null);

                if (l.mReferenceIds != null) {
                    writeVariable("'ReferenceIds'", l.mReferenceIds);
                }
                mWriter.write(" />\n");
            }
            mWriter.write("</ConstraintSet>\n");
        }

        private static final String SPACE = "\n       ";

        private void writeBoolen(String dimString, boolean val, boolean def) throws IOException {
            if (val != def) {
                mWriter.write(SPACE + dimString + "=\"" + val + "dp\"");
            }
        }

        private void writeEnum(String dimString,
                               int val,
                               String[] types,
                               int def) throws IOException {
            if (val != def) {
                mWriter.write(SPACE + dimString + "=\"" + types[val] + "\"");
            }
        }

        private void writeDimension(String dimString, int dim, int def) throws IOException {
            if (dim != def) {
                mWriter.write(SPACE + dimString + "=\"" + dim + "dp\"");
            }
        }

        private void writeBaseDimension(String dimString, int dim, int def) throws IOException {
            if (dim != def) {
                if (dim == -2) {
                    mWriter.write(SPACE + dimString + "=\"wrap_content\"");

                } else if (dim == -1) {
                    mWriter.write(SPACE + dimString + "=\"match_parent\"");

                } else {
                    mWriter.write(SPACE + dimString + "=\"" + dim + "dp\"");
                }
            }
        }

        HashMap<Integer, String> mIdMap = new HashMap<>();

        String getName(int id) {
            if (mIdMap.containsKey(id)) {
                return "@+id/" + mIdMap.get(id) + "";
            }
            if (id == 0) {
                return "parent";
            }
            String name = lookup(id);
            mIdMap.put(id, name);
            return "@+id/" + name + "";
        }

        String lookup(int id) {
            try {
                if (id != -1) {
                    return mContext.getResources().getResourceEntryName(id);
                } else {
                    return "unknown" + ++mUnknownCount;
                }
            } catch (Exception ex) {
                return "unknown" + ++mUnknownCount;
            }
        }

        void writeXmlConstraint(String str, int leftToLeft) throws IOException {
            if (leftToLeft == UNSET) {
                return;
            }
            mWriter.write(SPACE + str);
            mWriter.write("=\"" + getName(leftToLeft) + "\"");

        }

        void writeConstraint(String my, int leftToLeft,
                             String other,
                             int margin,
                             int goneMargin) throws IOException {
            if (leftToLeft == UNSET) {
                return;
            }
            mWriter.write(SPACE + my);
            mWriter.write(":[");
            mWriter.write(getName(leftToLeft));
            mWriter.write(" , ");
            mWriter.write(other);
            if (margin != 0) {
                mWriter.write(" , " + margin);
            }
            mWriter.write("],\n");

        }

        void writeCircle(int circleConstraint,
                         float circleAngle,
                         int circleRadius) throws IOException {
            if (circleConstraint == UNSET) {
                return;
            }
            mWriter.write("circle");
            mWriter.write(":[");
            mWriter.write(getName(circleConstraint));
            mWriter.write(", " + circleAngle);
            mWriter.write(circleRadius + "]");
        }

        void writeVariable(String name, int value) throws IOException {
            if (value == 0 || value == -1) {
                return;
            }
            mWriter.write(SPACE + name + "=\"" + value + "\"\n");
        }

        void writeVariable(String name, float value, float def) throws IOException {
            if (value == def) {
                return;
            }
            mWriter.write(SPACE + name);
            mWriter.write("=\"" + value + "\"");

        }

        void writeVariable(String name, String value, String def) throws IOException {
            if (value == null || value.equals(def)) {
                return;
            }
            mWriter.write(SPACE + name);
            mWriter.write("=\"" + value + "\"");

        }

        void writeVariable(String name, int[] value) throws IOException {
            if (value == null) {
                return;
            }
            mWriter.write(SPACE + name);
            mWriter.write(":");
            for (int i = 0; i < value.length; i++) {
                mWriter.write(((i == 0) ? "[" : ", ") + getName(value[i]));
            }
            mWriter.write("],\n");
        }

        void writeVariable(String name, String value) throws IOException {
            if (value == null) {
                return;
            }
            mWriter.write(name);
            mWriter.write(":");
            mWriter.write(", " + value);
            mWriter.write("\n");

        }
    }

    // ================================== JSON ===============================================
    class WriteJsonEngine {
        Writer mWriter;
        ConstraintLayout mLayout;
        Context mContext;
        int mFlags;
        int mUnknownCount = 0;
        final String mLEFT = "'left'";
        final String mRIGHT = "'right'";
        final String mBASELINE = "'baseline'";
        final String mBOTTOM = "'bottom'";
        final String mTOP = "'top'";
        final String mSTART = "'start'";
        final String mEND = "'end'";
        private static final String SPACE = "       ";

        WriteJsonEngine(Writer writer, ConstraintLayout layout, int flags) throws IOException {
            this.mWriter = writer;
            this.mLayout = layout;
            this.mContext = layout.getContext();
            this.mFlags = flags;
        }

        void writeLayout() throws IOException {
            mWriter.write("\n\'ConstraintSet\':{\n");
            for (Integer id : mConstraints.keySet()) {
                Constraint c = mConstraints.get(id);
                String idName = getName(id);
                mWriter.write(idName + ":{\n");
                Layout l = c.layout;

                writeDimension("height", l.mHeight, l.heightDefault, l.heightPercent,
                        l.heightMin, l.heightMax, l.constrainedHeight);
                writeDimension("width", l.mWidth, l.widthDefault, l.widthPercent,
                        l.widthMin, l.widthMax, l.constrainedWidth);

                writeConstraint(mLEFT, l.leftToLeft, mLEFT, l.leftMargin, l.goneLeftMargin);
                writeConstraint(mLEFT, l.leftToRight, mRIGHT, l.leftMargin, l.goneLeftMargin);
                writeConstraint(mRIGHT, l.rightToLeft, mLEFT, l.rightMargin, l.goneRightMargin);
                writeConstraint(mRIGHT, l.rightToRight, mRIGHT, l.rightMargin, l.goneRightMargin);
                writeConstraint(mBASELINE, l.baselineToBaseline, mBASELINE, UNSET,
                        l.goneBaselineMargin);
                writeConstraint(mBASELINE, l.baselineToTop, mTOP, UNSET, l.goneBaselineMargin);
                writeConstraint(mBASELINE, l.baselineToBottom,
                        mBOTTOM, UNSET, l.goneBaselineMargin);

                writeConstraint(mTOP, l.topToBottom, mBOTTOM, l.topMargin, l.goneTopMargin);
                writeConstraint(mTOP, l.topToTop, mTOP, l.topMargin, l.goneTopMargin);
                writeConstraint(mBOTTOM, l.bottomToBottom, mBOTTOM, l.bottomMargin,
                        l.goneBottomMargin);
                writeConstraint(mBOTTOM, l.bottomToTop, mTOP, l.bottomMargin, l.goneBottomMargin);
                writeConstraint(mSTART, l.startToStart, mSTART, l.startMargin, l.goneStartMargin);
                writeConstraint(mSTART, l.startToEnd, mEND, l.startMargin, l.goneStartMargin);
                writeConstraint(mEND, l.endToStart, mSTART, l.endMargin, l.goneEndMargin);
                writeConstraint(mEND, l.endToEnd, mEND, l.endMargin, l.goneEndMargin);
                writeVariable("'horizontalBias'", l.horizontalBias, 0.5f);
                writeVariable("'verticalBias'", l.verticalBias, 0.5f);

                writeCircle(l.circleConstraint, l.circleAngle, l.circleRadius);

                writeGuideline(l.orientation, l.guideBegin, l.guideEnd, l.guidePercent);
                writeVariable("'dimensionRatio'", l.dimensionRatio);
                writeVariable("'barrierMargin'", l.mBarrierMargin);
                writeVariable("'type'", l.mHelperType);
                writeVariable("'ReferenceId'", l.mReferenceIdString);
                writeVariable("'mBarrierAllowsGoneWidgets'",
                        l.mBarrierAllowsGoneWidgets, true);
                writeVariable("'WrapBehavior'", l.mWrapBehavior);

                writeVariable("'verticalWeight'", l.verticalWeight);
                writeVariable("'horizontalWeight'", l.horizontalWeight);
                writeVariable("'horizontalChainStyle'", l.horizontalChainStyle);
                writeVariable("'verticalChainStyle'", l.verticalChainStyle);
                writeVariable("'barrierDirection'", l.mBarrierDirection);
                if (l.mReferenceIds != null) {
                    writeVariable("'ReferenceIds'", l.mReferenceIds);
                }
                mWriter.write("}\n");
            }
            mWriter.write("}\n");
        }

        private void writeGuideline(int orientation,
                                    int guideBegin,
                                    int guideEnd,
                                    float guidePercent) throws IOException {
            writeVariable("'orientation'", orientation);
            writeVariable("'guideBegin'", guideBegin);
            writeVariable("'guideEnd'", guideEnd);
            writeVariable("'guidePercent'", guidePercent);

        }


        private void writeDimension(String dimString,
                                    int dim,
                                    int dimDefault,
                                    float dimPercent,
                                    int dimMin,
                                    int dimMax,
                                    boolean unusedConstrainedDim) throws IOException {
            if (dim == 0) {
                if (dimMax != UNSET || dimMin != UNSET) {
                    switch (dimDefault) {
                        case 0: // spread
                            mWriter.write(SPACE + dimString
                                    + ": {'spread' ," + dimMin + ", " + dimMax + "}\n");
                            break;
                        case 1: //  wrap
                            mWriter.write(SPACE + dimString
                                    + ": {'wrap' ," + dimMin + ", " + dimMax + "}\n");
                            return;
                        case 2: // percent
                            mWriter.write(SPACE + dimString + ": {'" + dimPercent
                                    + "'% ," + dimMin + ", " + dimMax + "}\n");
                            return;
                    }
                    return;
                }

                switch (dimDefault) {
                    case 0: // spread is the default
                        break;
                    case 1: //  wrap
                        mWriter.write(SPACE + dimString + ": '???????????',\n");
                        return;
                    case 2: // percent
                        mWriter.write(SPACE + dimString + ": '" + dimPercent + "%',\n");
                        return;
                }

            } else if (dim == -2) {
                mWriter.write(SPACE + dimString + ": 'wrap'\n");
            } else if (dim == -1) {
                mWriter.write(SPACE + dimString + ": 'parent'\n");
            } else {
                mWriter.write(SPACE + dimString + ": " + dim + ",\n");
            }
        }

        HashMap<Integer, String> mIdMap = new HashMap<>();

        String getName(int id) {
            if (mIdMap.containsKey(id)) {
                return "\'" + mIdMap.get(id) + "\'";
            }
            if (id == 0) {
                return "'parent'";
            }
            String name = lookup(id);
            mIdMap.put(id, name);
            return "\'" + name + "\'";
        }

        String lookup(int id) {
            try {
                if (id != -1) {
                    return mContext.getResources().getResourceEntryName(id);
                } else {
                    return "unknown" + ++mUnknownCount;
                }
            } catch (Exception ex) {
                return "unknown" + ++mUnknownCount;
            }
        }

        void writeConstraint(String my,
                             int leftToLeft,
                             String other,
                             int margin,
                             int goneMargin) throws IOException {
            if (leftToLeft == UNSET) {
                return;
            }
            mWriter.write(SPACE + my);
            mWriter.write(":[");
            mWriter.write(getName(leftToLeft));
            mWriter.write(" , ");
            mWriter.write(other);
            if (margin != 0) {
                mWriter.write(" , " + margin);
            }
            mWriter.write("],\n");

        }

        void writeCircle(int circleConstraint,
                         float circleAngle,
                         int circleRadius) throws IOException {
            if (circleConstraint == UNSET) {
                return;
            }
            mWriter.write(SPACE + "circle");
            mWriter.write(":[");
            mWriter.write(getName(circleConstraint));
            mWriter.write(", " + circleAngle);
            mWriter.write(circleRadius + "]");
        }

        void writeVariable(String name, int value) throws IOException {
            if (value == 0 || value == -1) {
                return;
            }
            mWriter.write(SPACE + name);
            mWriter.write(":");

            mWriter.write(", " + value);
            mWriter.write("\n");

        }

        void writeVariable(String name, float value) throws IOException {
            if (value == UNSET) {
                return;
            }
            mWriter.write(SPACE + name);

            mWriter.write(": " + value);
            mWriter.write(",\n");

        }

        void writeVariable(String name, float value, float def) throws IOException {
            if (value == def) {
                return;
            }
            mWriter.write(SPACE + name);

            mWriter.write(": " + value);
            mWriter.write(",\n");

        }

        void writeVariable(String name, boolean value) throws IOException {
            if (!value) {
                return;
            }
            mWriter.write(SPACE + name);

            mWriter.write(": " + value);
            mWriter.write(",\n");

        }
        void writeVariable(String name, boolean value , boolean def) throws IOException {
            if (value == def) {
                return;
            }
            mWriter.write(SPACE + name);

            mWriter.write(": " + value);
            mWriter.write(",\n");

        }

        void writeVariable(String name, int[] value) throws IOException {
            if (value == null) {
                return;
            }
            mWriter.write(SPACE + name);
            mWriter.write(": ");
            for (int i = 0; i < value.length; i++) {
                mWriter.write(((i == 0) ? "[" : ", ") + getName(value[i]));
            }
            mWriter.write("],\n");
        }

        void writeVariable(String name, String value) throws IOException {
            if (value == null) {
                return;
            }
            mWriter.write(SPACE + name);
            mWriter.write(":");
            mWriter.write(", " + value);
            mWriter.write("\n");

        }
    }

}