/// <param name="parent">May be null.</param>
		public BoneData (String name, BoneData parent) {
			if (name == null) throw new ArgumentNullException("name cannot be null.");
			this.name = name;
			this.parent = parent;
		}
		/// <param name="parent">May be null.</param>
		public Bone (BoneData data, Bone parent) {
			if (data == null) throw new ArgumentNullException("data cannot be null.");
			this.data = data;
			this.parent = parent;
			SetToSetupPose();
		}
		public SlotData (String name, BoneData boneData) {
			if (name == null) throw new ArgumentNullException("name cannot be null.");
			if (boneData == null) throw new ArgumentNullException("boneData cannot be null.");
			this.name = name;
			this.boneData = boneData;
		}
		public SkeletonData ReadSkeletonData (TextReader reader) {
			if (reader == null) throw new ArgumentNullException("reader cannot be null.");

			SkeletonData skeletonData = new SkeletonData();

			var root = Json.Deserialize(reader) as Dictionary<String, Object>;
			if (root == null) throw new Exception("Invalid JSON.");

			// Bones.
			foreach (Dictionary<String, Object> boneMap in (List<Object>)root["bones"]) {
				BoneData parent = null;
				if (boneMap.ContainsKey("parent")) {
					parent = skeletonData.FindBone((String)boneMap["parent"]);
					if (parent == null)
						throw new Exception("Parent bone not found: " + boneMap["parent"]);
				}
				BoneData boneData = new BoneData((String)boneMap["name"], parent);
				boneData.length = GetFloat(boneMap, "length", 0) * Scale;
				boneData.x = GetFloat(boneMap, "x", 0) * Scale;
				boneData.y = GetFloat(boneMap, "y", 0) * Scale;
				boneData.rotation = GetFloat(boneMap, "rotation", 0);
				boneData.scaleX = GetFloat(boneMap, "scaleX", 1);
				boneData.scaleY = GetFloat(boneMap, "scaleY", 1);
				boneData.inheritScale = GetBoolean(boneMap, "inheritScale", true);
				boneData.inheritRotation = GetBoolean(boneMap, "inheritRotation", true);
				skeletonData.AddBone(boneData);
			}

			// Slots.
			if (root.ContainsKey("slots")) {
				foreach (Dictionary<String, Object> slotMap in (List<Object>)root["slots"]) {
					String slotName = (String)slotMap["name"];
					String boneName = (String)slotMap["bone"];
					BoneData boneData = skeletonData.FindBone(boneName);
					if (boneData == null)
						throw new Exception("Slot bone not found: " + boneName);
					SlotData slotData = new SlotData(slotName, boneData);

					if (slotMap.ContainsKey("color")) {
						String color = (String)slotMap["color"];
						slotData.r = ToColor(color, 0);
						slotData.g = ToColor(color, 1);
						slotData.b = ToColor(color, 2);
						slotData.a = ToColor(color, 3);
					}

					if (slotMap.ContainsKey("attachment"))
						slotData.attachmentName = (String)slotMap["attachment"];

					if (slotMap.ContainsKey("additive"))
						slotData.additiveBlending = (bool)slotMap["additive"];

					skeletonData.AddSlot(slotData);
				}
			}

			// Skins.
			if (root.ContainsKey("skins")) {
				foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["skins"]) {
					Skin skin = new Skin(entry.Key);
					foreach (KeyValuePair<String, Object> slotEntry in (Dictionary<String, Object>)entry.Value) {
						int slotIndex = skeletonData.FindSlotIndex(slotEntry.Key);
						foreach (KeyValuePair<String, Object> attachmentEntry in ((Dictionary<String, Object>)slotEntry.Value)) {
							Attachment attachment = ReadAttachment(skin, attachmentEntry.Key, (Dictionary<String, Object>)attachmentEntry.Value);
							if (attachment != null) skin.AddAttachment(slotIndex, attachmentEntry.Key, attachment);
						}
					}
					skeletonData.AddSkin(skin);
					if (skin.name == "default")
						skeletonData.defaultSkin = skin;
				}
			}

			// Events.
			if (root.ContainsKey("events")) {
				foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["events"]) {
					var entryMap = (Dictionary<String, Object>)entry.Value;
					EventData eventData = new EventData(entry.Key);
					eventData.Int = GetInt(entryMap, "int", 0);
					eventData.Float = GetFloat(entryMap, "float", 0);
					eventData.String = GetString(entryMap, "string", null);
					skeletonData.AddEvent(eventData);
				}
			}

			// Animations.
			if (root.ContainsKey("animations")) {
				foreach (KeyValuePair<String, Object> entry in (Dictionary<String, Object>)root["animations"])
					ReadAnimation(entry.Key, (Dictionary<String, Object>)entry.Value, skeletonData);
			}

			skeletonData.bones.TrimExcess();
			skeletonData.slots.TrimExcess();
			skeletonData.skins.TrimExcess();
			skeletonData.animations.TrimExcess();
			return skeletonData;
		}
		// --- Bones.

		public void AddBone (BoneData bone) {
			if (bone == null) throw new ArgumentNullException("bone cannot be null.");
			bones.Add(bone);
		}