/* * Catroid: An on-device visual programming system for Android devices * Copyright (C) 2010-2016 The Catrobat Team * (<http://developer.catrobat.org/credits>) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * An additional term exception under section 7 of the GNU Affero * General Public License, version 3, is available at * http://developer.catrobat.org/license_additional_term * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.catrobat.catroid.io; import android.util.Log; import com.thoughtworks.xstream.XStream; import com.thoughtworks.xstream.converters.ConversionException; import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider; import org.catrobat.catroid.content.BroadcastScript; import org.catrobat.catroid.content.CollisionScript; import org.catrobat.catroid.content.RaspiInterruptScript; import org.catrobat.catroid.content.StartScript; import org.catrobat.catroid.content.WhenBackgroundChangesScript; import org.catrobat.catroid.content.WhenConditionScript; import org.catrobat.catroid.content.WhenNfcScript; import org.catrobat.catroid.content.WhenScript; import org.catrobat.catroid.content.WhenTouchDownScript; import org.catrobat.catroid.content.bricks.ArduinoSendDigitalValueBrick; import org.catrobat.catroid.content.bricks.ArduinoSendPWMValueBrick; import org.catrobat.catroid.content.bricks.AskBrick; import org.catrobat.catroid.content.bricks.AskSpeechBrick; import org.catrobat.catroid.content.bricks.Brick.BrickField; import org.catrobat.catroid.content.bricks.BroadcastBrick; import org.catrobat.catroid.content.bricks.BroadcastReceiverBrick; import org.catrobat.catroid.content.bricks.BroadcastWaitBrick; import org.catrobat.catroid.content.bricks.CameraBrick; import org.catrobat.catroid.content.bricks.ChangeBrightnessByNBrick; import org.catrobat.catroid.content.bricks.ChangeColorByNBrick; import org.catrobat.catroid.content.bricks.ChangeSizeByNBrick; import org.catrobat.catroid.content.bricks.ChangeTransparencyByNBrick; import org.catrobat.catroid.content.bricks.ChangeVariableBrick; import org.catrobat.catroid.content.bricks.ChangeVolumeByNBrick; import org.catrobat.catroid.content.bricks.ChangeXByNBrick; import org.catrobat.catroid.content.bricks.ChangeYByNBrick; import org.catrobat.catroid.content.bricks.ChooseCameraBrick; import org.catrobat.catroid.content.bricks.ClearBackgroundBrick; import org.catrobat.catroid.content.bricks.ClearGraphicEffectBrick; import org.catrobat.catroid.content.bricks.CloneBrick; import org.catrobat.catroid.content.bricks.ComeToFrontBrick; import org.catrobat.catroid.content.bricks.DeleteThisCloneBrick; import org.catrobat.catroid.content.bricks.DroneEmergencyBrick; import org.catrobat.catroid.content.bricks.DroneFlipBrick; import org.catrobat.catroid.content.bricks.DroneMoveBackwardBrick; import org.catrobat.catroid.content.bricks.DroneMoveDownBrick; import org.catrobat.catroid.content.bricks.DroneMoveForwardBrick; import org.catrobat.catroid.content.bricks.DroneMoveLeftBrick; import org.catrobat.catroid.content.bricks.DroneMoveRightBrick; import org.catrobat.catroid.content.bricks.DroneMoveUpBrick; import org.catrobat.catroid.content.bricks.DronePlayLedAnimationBrick; import org.catrobat.catroid.content.bricks.DroneSwitchCameraBrick; import org.catrobat.catroid.content.bricks.DroneTakeOffLandBrick; import org.catrobat.catroid.content.bricks.DroneTurnLeftBrick; import org.catrobat.catroid.content.bricks.DroneTurnRightBrick; import org.catrobat.catroid.content.bricks.FlashBrick; import org.catrobat.catroid.content.bricks.ForeverBrick; import org.catrobat.catroid.content.bricks.GlideToBrick; import org.catrobat.catroid.content.bricks.GoNStepsBackBrick; import org.catrobat.catroid.content.bricks.GoToBrick; import org.catrobat.catroid.content.bricks.HideBrick; import org.catrobat.catroid.content.bricks.HideTextBrick; import org.catrobat.catroid.content.bricks.IfLogicBeginBrick; import org.catrobat.catroid.content.bricks.IfLogicElseBrick; import org.catrobat.catroid.content.bricks.IfLogicEndBrick; import org.catrobat.catroid.content.bricks.IfOnEdgeBounceBrick; import org.catrobat.catroid.content.bricks.IfThenLogicBeginBrick; import org.catrobat.catroid.content.bricks.IfThenLogicEndBrick; import org.catrobat.catroid.content.bricks.LegoNxtMotorMoveBrick; import org.catrobat.catroid.content.bricks.LegoNxtMotorStopBrick; import org.catrobat.catroid.content.bricks.LegoNxtMotorTurnAngleBrick; import org.catrobat.catroid.content.bricks.LegoNxtPlayToneBrick; import org.catrobat.catroid.content.bricks.LoopEndBrick; import org.catrobat.catroid.content.bricks.LoopEndlessBrick; import org.catrobat.catroid.content.bricks.MoveNStepsBrick; import org.catrobat.catroid.content.bricks.NextLookBrick; import org.catrobat.catroid.content.bricks.NoteBrick; import org.catrobat.catroid.content.bricks.PenDownBrick; import org.catrobat.catroid.content.bricks.PenUpBrick; import org.catrobat.catroid.content.bricks.PhiroIfLogicBeginBrick; import org.catrobat.catroid.content.bricks.PhiroMotorMoveBackwardBrick; import org.catrobat.catroid.content.bricks.PhiroMotorMoveForwardBrick; import org.catrobat.catroid.content.bricks.PhiroMotorStopBrick; import org.catrobat.catroid.content.bricks.PhiroPlayToneBrick; import org.catrobat.catroid.content.bricks.PhiroRGBLightBrick; import org.catrobat.catroid.content.bricks.PlaceAtBrick; import org.catrobat.catroid.content.bricks.PlaySoundAndWaitBrick; import org.catrobat.catroid.content.bricks.PlaySoundBrick; import org.catrobat.catroid.content.bricks.PointInDirectionBrick; import org.catrobat.catroid.content.bricks.PointToBrick; import org.catrobat.catroid.content.bricks.PreviousLookBrick; import org.catrobat.catroid.content.bricks.RaspiIfLogicBeginBrick; import org.catrobat.catroid.content.bricks.RaspiPwmBrick; import org.catrobat.catroid.content.bricks.RaspiSendDigitalValueBrick; import org.catrobat.catroid.content.bricks.RepeatBrick; import org.catrobat.catroid.content.bricks.SayBubbleBrick; import org.catrobat.catroid.content.bricks.SayForBubbleBrick; import org.catrobat.catroid.content.bricks.SceneStartBrick; import org.catrobat.catroid.content.bricks.SceneTransitionBrick; import org.catrobat.catroid.content.bricks.SetBackgroundAndWaitBrick; import org.catrobat.catroid.content.bricks.SetBackgroundBrick; import org.catrobat.catroid.content.bricks.SetBrightnessBrick; import org.catrobat.catroid.content.bricks.SetColorBrick; import org.catrobat.catroid.content.bricks.SetLookBrick; import org.catrobat.catroid.content.bricks.SetNfcTagBrick; import org.catrobat.catroid.content.bricks.SetPenColorBrick; import org.catrobat.catroid.content.bricks.SetPenSizeBrick; import org.catrobat.catroid.content.bricks.SetRotationStyleBrick; import org.catrobat.catroid.content.bricks.SetSizeToBrick; import org.catrobat.catroid.content.bricks.SetTextBrick; import org.catrobat.catroid.content.bricks.SetTransparencyBrick; import org.catrobat.catroid.content.bricks.SetVariableBrick; import org.catrobat.catroid.content.bricks.SetVolumeToBrick; import org.catrobat.catroid.content.bricks.SetXBrick; import org.catrobat.catroid.content.bricks.SetYBrick; import org.catrobat.catroid.content.bricks.ShowBrick; import org.catrobat.catroid.content.bricks.ShowTextBrick; import org.catrobat.catroid.content.bricks.SpeakAndWaitBrick; import org.catrobat.catroid.content.bricks.SpeakBrick; import org.catrobat.catroid.content.bricks.StampBrick; import org.catrobat.catroid.content.bricks.StopAllSoundsBrick; import org.catrobat.catroid.content.bricks.StopScriptBrick; import org.catrobat.catroid.content.bricks.ThinkBubbleBrick; import org.catrobat.catroid.content.bricks.ThinkForBubbleBrick; import org.catrobat.catroid.content.bricks.TurnLeftBrick; import org.catrobat.catroid.content.bricks.TurnRightBrick; import org.catrobat.catroid.content.bricks.VibrationBrick; import org.catrobat.catroid.content.bricks.WaitBrick; import org.catrobat.catroid.content.bricks.WhenBrick; import org.catrobat.catroid.content.bricks.WhenClonedBrick; import org.catrobat.catroid.content.bricks.WhenConditionBrick; import org.catrobat.catroid.content.bricks.WhenNfcBrick; import org.catrobat.catroid.content.bricks.WhenStartedBrick; import org.catrobat.catroid.physics.PhysicsCollision; import org.catrobat.catroid.physics.content.bricks.CollisionReceiverBrick; import org.catrobat.catroid.physics.content.bricks.SetBounceBrick; import org.catrobat.catroid.physics.content.bricks.SetFrictionBrick; import org.catrobat.catroid.physics.content.bricks.SetGravityBrick; import org.catrobat.catroid.physics.content.bricks.SetMassBrick; import org.catrobat.catroid.physics.content.bricks.SetPhysicsObjectTypeBrick; import org.catrobat.catroid.physics.content.bricks.SetVelocityBrick; import org.catrobat.catroid.physics.content.bricks.TurnLeftSpeedBrick; import org.catrobat.catroid.physics.content.bricks.TurnRightSpeedBrick; import org.w3c.dom.Attr; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import java.io.File; import java.util.HashMap; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.transform.OutputKeys; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; public class BackwardCompatibleCatrobatLanguageXStream extends XStream { private static final String TAG = BackwardCompatibleCatrobatLanguageXStream.class.getSimpleName(); private HashMap<String, BrickInfo> brickInfoMap; private HashMap<String, String> scriptInfoMap; public BackwardCompatibleCatrobatLanguageXStream(PureJavaReflectionProvider reflectionProvider) { super(reflectionProvider); } public Object getProjectFromXML(File file) { Object parsedObject; try { parsedObject = super.fromXML(file); } catch (ConversionException exception) { Log.e(TAG, "Conversion error " + exception.getLocalizedMessage()); modifyXMLToSupportUnknownFields(file); parsedObject = super.fromXML(file); } return parsedObject; } private void initializeBrickInfoMap() { if (brickInfoMap != null) { return; } brickInfoMap = new HashMap<>(); BrickInfo brickInfo = new BrickInfo(AskBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("askQuestion", BrickField.ASK_QUESTION); brickInfoMap.put("askBrick", brickInfo); brickInfo = new BrickInfo(AskSpeechBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("askQuestionSpoken", BrickField.ASK_SPEECH_QUESTION); brickInfoMap.put("askSpeechBrick", brickInfo); brickInfo = new BrickInfo(BroadcastBrick.class.getSimpleName()); brickInfoMap.put("broadcastBrick", brickInfo); brickInfo = new BrickInfo(BroadcastReceiverBrick.class.getSimpleName()); brickInfoMap.put("broadcastReceiverBrick", brickInfo); brickInfo = new BrickInfo(BroadcastWaitBrick.class.getSimpleName()); brickInfoMap.put("broadcastWaitBrick", brickInfo); brickInfo = new BrickInfo(ChangeBrightnessByNBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("changeBrightness", BrickField.BRIGHTNESS_CHANGE); brickInfoMap.put("changeBrightnessByNBrick", brickInfo); brickInfo = new BrickInfo(ChangeColorByNBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("changeColor", BrickField.COLOR_CHANGE); brickInfoMap.put("changeColorByNBrick", brickInfo); brickInfo = new BrickInfo(ChangeTransparencyByNBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("changeTransparency", BrickField.TRANSPARENCY_CHANGE); brickInfoMap.put("changeTransparencyByNBrick", brickInfo); brickInfo = new BrickInfo(ChangeSizeByNBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("size", BrickField.SIZE_CHANGE); brickInfoMap.put("changeSizeByNBrick", brickInfo); brickInfo = new BrickInfo(ChangeVariableBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("variableFormula", BrickField.VARIABLE_CHANGE); brickInfoMap.put("changeVariableBrick", brickInfo); brickInfo = new BrickInfo(ChangeVolumeByNBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("volume", BrickField.VOLUME_CHANGE); brickInfoMap.put("changeVolumeByNBrick", brickInfo); brickInfo = new BrickInfo(ChangeXByNBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("xMovement", BrickField.X_POSITION_CHANGE); brickInfoMap.put("changeXByNBrick", brickInfo); brickInfo = new BrickInfo(ChangeYByNBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("yMovement", BrickField.Y_POSITION_CHANGE); brickInfoMap.put("changeYByNBrick", brickInfo); brickInfo = new BrickInfo(ClearGraphicEffectBrick.class.getSimpleName()); brickInfoMap.put("clearGraphicEffectBrick", brickInfo); brickInfo = new BrickInfo(ComeToFrontBrick.class.getSimpleName()); brickInfoMap.put("comeToFrontBrick", brickInfo); brickInfo = new BrickInfo(ForeverBrick.class.getSimpleName()); brickInfoMap.put("foreverBrick", brickInfo); brickInfo = new BrickInfo(GlideToBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("xDestination", BrickField.X_DESTINATION); brickInfo.addBrickFieldToMap("yDestination", BrickField.Y_DESTINATION); brickInfo.addBrickFieldToMap("durationInSeconds", BrickField.DURATION_IN_SECONDS); brickInfoMap.put("glideToBrick", brickInfo); brickInfo = new BrickInfo(GoNStepsBackBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("steps", BrickField.STEPS); brickInfoMap.put("goNStepsBackBrick", brickInfo); brickInfo = new BrickInfo(HideBrick.class.getSimpleName()); brickInfoMap.put("hideBrick", brickInfo); brickInfo = new BrickInfo(IfLogicBeginBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("ifCondition", BrickField.IF_CONDITION); brickInfoMap.put("ifLogicBeginBrick", brickInfo); brickInfo = new BrickInfo(IfLogicElseBrick.class.getSimpleName()); brickInfoMap.put("ifLogicElseBrick", brickInfo); brickInfo = new BrickInfo(IfLogicEndBrick.class.getSimpleName()); brickInfoMap.put("ifLogicEndBrick", brickInfo); brickInfo = new BrickInfo(IfThenLogicBeginBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("ifCondition", BrickField.IF_CONDITION); brickInfoMap.put("ifThenLogicBeginBrick", brickInfo); brickInfo = new BrickInfo(IfThenLogicEndBrick.class.getSimpleName()); brickInfoMap.put("ifThenLogicEndBrick", brickInfo); brickInfo = new BrickInfo(IfOnEdgeBounceBrick.class.getSimpleName()); brickInfoMap.put("ifOnEdgeBounceBrick", brickInfo); brickInfo = new BrickInfo(FlashBrick.class.getSimpleName()); brickInfoMap.put("flashBrick", brickInfo); brickInfo = new BrickInfo(LegoNxtMotorMoveBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("speed", BrickField.LEGO_NXT_SPEED); brickInfoMap.put("legoNxtMotorMoveBrick", brickInfo); brickInfo = new BrickInfo(LegoNxtMotorStopBrick.class.getSimpleName()); brickInfoMap.put("legoNxtMotorStopBrick", brickInfo); brickInfo = new BrickInfo(LegoNxtMotorTurnAngleBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("degrees", BrickField.LEGO_NXT_DEGREES); brickInfoMap.put("legoNxtMotorTurnAngleBrick", brickInfo); brickInfo = new BrickInfo(LegoNxtPlayToneBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("frequency", BrickField.LEGO_NXT_FREQUENCY); brickInfo.addBrickFieldToMap("durationInSeconds", BrickField.LEGO_NXT_DURATION_IN_SECONDS); brickInfoMap.put("legoNxtPlayToneBrick", brickInfo); brickInfo = new BrickInfo(PhiroMotorMoveForwardBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("speed", BrickField.PHIRO_SPEED); brickInfoMap.put("phiroMotorMoveForwardBrick", brickInfo); brickInfo = new BrickInfo(PhiroMotorMoveBackwardBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("speed", BrickField.PHIRO_SPEED); brickInfoMap.put("phiroMotorMoveBackwardBrick", brickInfo); brickInfo = new BrickInfo(PhiroMotorStopBrick.class.getSimpleName()); brickInfoMap.put("phiroMotorStopBrick", brickInfo); brickInfo = new BrickInfo(PhiroPlayToneBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("durationInSeconds", BrickField.PHIRO_DURATION_IN_SECONDS); brickInfoMap.put("phiroPlayToneBrick", brickInfo); brickInfo = new BrickInfo(PhiroRGBLightBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("light", BrickField.PHIRO_LIGHT_RED); brickInfo.addBrickFieldToMap("light", BrickField.PHIRO_LIGHT_GREEN); brickInfo.addBrickFieldToMap("light", BrickField.PHIRO_LIGHT_BLUE); brickInfoMap.put("phiroRGBLightBrick", brickInfo); brickInfo = new BrickInfo(PhiroIfLogicBeginBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("ifPhiroSensorCondition", BrickField.IF_PHIRO_SENSOR_CONDITION); brickInfoMap.put("phiroSensorBrick", brickInfo); brickInfo = new BrickInfo(IfLogicElseBrick.class.getSimpleName()); brickInfoMap.put("phiroSensorElseBrick", brickInfo); brickInfo = new BrickInfo(IfLogicEndBrick.class.getSimpleName()); brickInfoMap.put("phiroSensorEndBrick", brickInfo); brickInfo = new BrickInfo(ArduinoSendDigitalValueBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("digitalPinNumber", BrickField.ARDUINO_DIGITAL_PIN_NUMBER); brickInfo.addBrickFieldToMap("digitalPinValue", BrickField.ARDUINO_DIGITAL_PIN_VALUE); brickInfoMap.put("arduinoSendDigitalValueBrick", brickInfo); brickInfo = new BrickInfo(ArduinoSendPWMValueBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("pwmPinNumber", BrickField.ARDUINO_ANALOG_PIN_NUMBER); brickInfo.addBrickFieldToMap("pwmPinValue", BrickField.ARDUINO_ANALOG_PIN_VALUE); brickInfoMap.put("arduinoSendPWMValueBrick", brickInfo); brickInfo = new BrickInfo(RaspiSendDigitalValueBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("digitalPinNumber", BrickField.RASPI_DIGITAL_PIN_NUMBER); brickInfo.addBrickFieldToMap("digitalPinValue", BrickField.RASPI_DIGITAL_PIN_VALUE); brickInfoMap.put("raspiSendDigitalValueBrick", brickInfo); brickInfo = new BrickInfo(RaspiPwmBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("digitalPinNumber", BrickField.RASPI_DIGITAL_PIN_NUMBER); brickInfo.addBrickFieldToMap("pwmFrequency", BrickField.RASPI_PWM_FREQUENCY); brickInfo.addBrickFieldToMap("pwmPercentage", BrickField.RASPI_PWM_PERCENTAGE); brickInfoMap.put("raspiPwmBrick", brickInfo); brickInfo = new BrickInfo(RaspiIfLogicBeginBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("digitalPinNumber", BrickField.IF_CONDITION); brickInfoMap.put("raspiIfLogicBeginBrick", brickInfo); brickInfo = new BrickInfo(LoopEndBrick.class.getSimpleName()); brickInfoMap.put("loopEndBrick", brickInfo); brickInfo = new BrickInfo(LoopEndlessBrick.class.getSimpleName()); brickInfoMap.put("loopEndlessBrick", brickInfo); brickInfo = new BrickInfo(MoveNStepsBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("steps", BrickField.STEPS); brickInfoMap.put("moveNStepsBrick", brickInfo); brickInfo = new BrickInfo(NextLookBrick.class.getSimpleName()); brickInfoMap.put("nextLookBrick", brickInfo); brickInfo = new BrickInfo(PreviousLookBrick.class.getSimpleName()); brickInfoMap.put("previousLookBrick", brickInfo); brickInfo = new BrickInfo(NoteBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("note", BrickField.NOTE); brickInfoMap.put("noteBrick", brickInfo); brickInfo = new BrickInfo(PlaceAtBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("xPosition", BrickField.X_POSITION); brickInfo.addBrickFieldToMap("yPosition", BrickField.Y_POSITION); brickInfoMap.put("placeAtBrick", brickInfo); brickInfo = new BrickInfo(GoToBrick.class.getSimpleName()); brickInfoMap.put("goToBrick", brickInfo); brickInfo = new BrickInfo(PlaySoundBrick.class.getSimpleName()); brickInfoMap.put("playSoundBrick", brickInfo); brickInfo = new BrickInfo(PlaySoundAndWaitBrick.class.getSimpleName()); brickInfoMap.put("playSoundAndWaitBrick", brickInfo); brickInfo = new BrickInfo(PointInDirectionBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("degrees", BrickField.DEGREES); brickInfoMap.put("pointInDirectionBrick", brickInfo); brickInfo = new BrickInfo(PointToBrick.class.getSimpleName()); brickInfoMap.put("pointToBrick", brickInfo); brickInfo = new BrickInfo(RepeatBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timesToRepeat", BrickField.TIMES_TO_REPEAT); brickInfoMap.put("repeatBrick", brickInfo); brickInfo = new BrickInfo(SceneTransitionBrick.class.getSimpleName()); brickInfoMap.put("sceneTransitionBrick", brickInfo); brickInfo = new BrickInfo(SceneStartBrick.class.getSimpleName()); brickInfoMap.put("sceneStartBrick", brickInfo); brickInfo = new BrickInfo(SetBrightnessBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("brightness", BrickField.BRIGHTNESS); brickInfoMap.put("setBrightnessBrick", brickInfo); brickInfo = new BrickInfo(SetColorBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("color", BrickField.COLOR); brickInfoMap.put("setColorBrick", brickInfo); brickInfo = new BrickInfo(SetTransparencyBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("transparency", BrickField.TRANSPARENCY); brickInfoMap.put("setTransparencyBrick", brickInfo); brickInfo = new BrickInfo(SetBackgroundBrick.class.getSimpleName()); brickInfoMap.put("setBackgroundBrick", brickInfo); brickInfo = new BrickInfo(SetBackgroundAndWaitBrick.class.getSimpleName()); brickInfoMap.put("setBackgroundAndWaitBrick", brickInfo); brickInfo = new BrickInfo(SetLookBrick.class.getSimpleName()); brickInfoMap.put("setLookBrick", brickInfo); brickInfo = new BrickInfo(SetRotationStyleBrick.class.getSimpleName()); brickInfoMap.put("setRotationStyleBrick", brickInfo); brickInfo = new BrickInfo(SetSizeToBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("size", BrickField.SIZE); brickInfoMap.put("setSizeToBrick", brickInfo); brickInfo = new BrickInfo(SetVariableBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("variableFormula", BrickField.VARIABLE); brickInfoMap.put("setVariableBrick", brickInfo); brickInfo = new BrickInfo(SetVolumeToBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("volume", BrickField.VOLUME); brickInfoMap.put("setVolumeToBrick", brickInfo); brickInfo = new BrickInfo(SetXBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("xPosition", BrickField.X_POSITION); brickInfoMap.put("setXBrick", brickInfo); brickInfo = new BrickInfo(SetYBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("yPosition", BrickField.Y_POSITION); brickInfoMap.put("setYBrick", brickInfo); brickInfo = new BrickInfo(ShowBrick.class.getSimpleName()); brickInfoMap.put("showBrick", brickInfo); brickInfo = new BrickInfo(SpeakBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("text", BrickField.SPEAK); brickInfoMap.put("speakBrick", brickInfo); brickInfo = new BrickInfo(ThinkBubbleBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("text", BrickField.STRING); brickInfoMap.put("thinkBubbleBrick", brickInfo); brickInfo = new BrickInfo(SayBubbleBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("text", BrickField.STRING); brickInfoMap.put("sayBubbleBrick", brickInfo); brickInfo = new BrickInfo(ThinkForBubbleBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("text", BrickField.STRING); brickInfo.addBrickFieldToMap("durationInSeconds", BrickField.DURATION_IN_SECONDS); brickInfoMap.put("thinkForBubbleBrick", brickInfo); brickInfo = new BrickInfo(SayForBubbleBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("text", BrickField.STRING); brickInfo.addBrickFieldToMap("durationInSeconds", BrickField.DURATION_IN_SECONDS); brickInfoMap.put("sayForBubbleBrick", brickInfo); brickInfo = new BrickInfo(SpeakAndWaitBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("text", BrickField.SPEAK); brickInfoMap.put("speakAndWaitBrick", brickInfo); brickInfo = new BrickInfo(WhenBrick.class.getSimpleName()); brickInfoMap.put("whenBrick", brickInfo); brickInfo = new BrickInfo(WhenConditionBrick.class.getSimpleName()); brickInfoMap.put("whenConditionBrick", brickInfo); brickInfo = new BrickInfo(TurnLeftBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("degrees", BrickField.TURN_LEFT_DEGREES); brickInfoMap.put("turnLeftBrick", brickInfo); brickInfo = new BrickInfo(TurnRightBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("degrees", BrickField.TURN_RIGHT_DEGREES); brickInfoMap.put("turnRightBrick", brickInfo); brickInfo = new BrickInfo(VibrationBrick.class.getSimpleName()); brickInfoMap.put("vibrationBrick", brickInfo); brickInfo = new BrickInfo(WaitBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToWaitInSeconds", BrickField.TIME_TO_WAIT_IN_SECONDS); brickInfoMap.put("waitBrick", brickInfo); brickInfo = new BrickInfo(StopAllSoundsBrick.class.getSimpleName()); brickInfoMap.put("stopAllSoundsBrick", brickInfo); brickInfo = new BrickInfo(WhenStartedBrick.class.getSimpleName()); brickInfoMap.put("whenStartedBrick", brickInfo); brickInfo = new BrickInfo(StopScriptBrick.class.getSimpleName()); brickInfoMap.put("stopScriptBrick", brickInfo); brickInfo = new BrickInfo(WhenNfcBrick.class.getSimpleName()); brickInfoMap.put("whenNfcBrick", brickInfo); brickInfo = new BrickInfo(SetNfcTagBrick.class.getSimpleName()); brickInfoMap.put("setNfcTagBrick", brickInfo); brickInfo = new BrickInfo(WhenClonedBrick.class.getSimpleName()); brickInfoMap.put("whenClonedBrick", brickInfo); brickInfo = new BrickInfo(CloneBrick.class.getSimpleName()); brickInfoMap.put("cloneBrick", brickInfo); brickInfo = new BrickInfo(DeleteThisCloneBrick.class.getSimpleName()); brickInfoMap.put("deleteThisCloneBrick", brickInfo); brickInfo = new BrickInfo(DronePlayLedAnimationBrick.class.getSimpleName()); brickInfoMap.put("dronePlayLedAnimationBrick", brickInfo); brickInfo = new BrickInfo(DroneFlipBrick.class.getSimpleName()); brickInfoMap.put("droneFlipBrick", brickInfo); brickInfo = new BrickInfo(DroneEmergencyBrick.class.getSimpleName()); brickInfoMap.put("droneGoEmergencyBrick", brickInfo); brickInfo = new BrickInfo(DroneTakeOffLandBrick.class.getSimpleName()); brickInfoMap.put("droneTakeOffBrick", brickInfo); brickInfo = new BrickInfo(DroneMoveForwardBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneMoveForwardBrick", brickInfo); brickInfo = new BrickInfo(DroneMoveBackwardBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneMoveBackwardBrick", brickInfo); brickInfo = new BrickInfo(DroneMoveUpBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneMoveUpBrick", brickInfo); brickInfo = new BrickInfo(DroneMoveDownBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneMoveDownBrick", brickInfo); brickInfo = new BrickInfo(DroneMoveLeftBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneMoveLeftBrick", brickInfo); brickInfo = new BrickInfo(DroneMoveRightBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneMoveRightBrick", brickInfo); brickInfo = new BrickInfo(DroneTurnLeftBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneTurnLeftBrick", brickInfo); brickInfo = new BrickInfo(DroneTurnRightBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("timeToFlyInSeconds", BrickField.DRONE_TIME_TO_FLY_IN_SECONDS); brickInfo.addBrickFieldToMap("powerInPercent", BrickField.DRONE_POWER_IN_PERCENT); brickInfoMap.put("droneTurnRightBrick", brickInfo); brickInfo = new BrickInfo(DroneSwitchCameraBrick.class.getSimpleName()); brickInfoMap.put("droneSwitchCameraBrick", brickInfo); brickInfo = new BrickInfo(DroneEmergencyBrick.class.getSimpleName()); brickInfoMap.put("DroneEmergencyBrick", brickInfo); brickInfo = new BrickInfo(SetTextBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("xDestination", BrickField.X_DESTINATION); brickInfo.addBrickFieldToMap("yDestination", BrickField.Y_DESTINATION); brickInfo.addBrickFieldToMap("string", BrickField.STRING); brickInfoMap.put("setTextBrick", brickInfo); brickInfo = new BrickInfo(ShowTextBrick.class.getSimpleName()); brickInfoMap.put("showTextBrick", brickInfo); brickInfo = new BrickInfo(HideTextBrick.class.getSimpleName()); brickInfoMap.put("hideTextBrick", brickInfo); brickInfo = new BrickInfo(CameraBrick.class.getSimpleName()); brickInfoMap.put("videoBrick", brickInfo); brickInfo = new BrickInfo(ChooseCameraBrick.class.getSimpleName()); brickInfoMap.put("chooseCameraBrick", brickInfo); brickInfo = new BrickInfo(CollisionReceiverBrick.class.getSimpleName()); brickInfoMap.put("collisionReceiverBrick", brickInfo); brickInfo = new BrickInfo(SetBounceBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("bounceFactor", BrickField.PHYSICS_BOUNCE_FACTOR); brickInfoMap.put("setBounceBrick", brickInfo); brickInfo = new BrickInfo(SetFrictionBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("friction", BrickField.PHYSICS_FRICTION); brickInfoMap.put("setFrictionBrick", brickInfo); brickInfo = new BrickInfo(SetGravityBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("gravityX", BrickField.PHYSICS_GRAVITY_X); brickInfo.addBrickFieldToMap("gravityY", BrickField.PHYSICS_GRAVITY_Y); brickInfoMap.put("setGravityBrick", brickInfo); brickInfo = new BrickInfo(SetMassBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("mass", BrickField.PHYSICS_MASS); brickInfoMap.put("setMassBrick", brickInfo); brickInfo = new BrickInfo(SetPhysicsObjectTypeBrick.class.getSimpleName()); brickInfoMap.put("setPhysicsObjectTypeBrick", brickInfo); brickInfo = new BrickInfo(SetVelocityBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("velocityX", BrickField.PHYSICS_VELOCITY_X); brickInfo.addBrickFieldToMap("velocityY", BrickField.PHYSICS_VELOCITY_Y); brickInfoMap.put("setVelocityBrick", brickInfo); brickInfo = new BrickInfo(TurnLeftSpeedBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("turnLeftSpeed", BrickField.PHYSICS_TURN_LEFT_SPEED); brickInfoMap.put("turnLeftSpeedBrick", brickInfo); brickInfo = new BrickInfo(TurnRightSpeedBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("turnRightSpeed", BrickField.PHYSICS_TURN_RIGHT_SPEED); brickInfoMap.put("turnRightSpeedBrick", brickInfo); brickInfo = new BrickInfo(PenDownBrick.class.getSimpleName()); brickInfoMap.put("penDownBrick", brickInfo); brickInfo = new BrickInfo(PenUpBrick.class.getSimpleName()); brickInfoMap.put("penUpBrick", brickInfo); brickInfo = new BrickInfo(StampBrick.class.getSimpleName()); brickInfoMap.put("stampBrick", brickInfo); brickInfo = new BrickInfo(ClearBackgroundBrick.class.getSimpleName()); brickInfoMap.put("clearBackgroundBrick", brickInfo); brickInfo = new BrickInfo(SetPenSizeBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("penSize", BrickField.PEN_SIZE); brickInfoMap.put("setPenSizeBrick", brickInfo); brickInfo = new BrickInfo(SetPenColorBrick.class.getSimpleName()); brickInfo.addBrickFieldToMap("penColor", BrickField.PHIRO_LIGHT_RED); brickInfo.addBrickFieldToMap("penColor", BrickField.PHIRO_LIGHT_GREEN); brickInfo.addBrickFieldToMap("penColor", BrickField.PHIRO_LIGHT_BLUE); brickInfoMap.put("setPenColorBrick", brickInfo); } private void initializeScriptInfoMap() { if (scriptInfoMap != null) { return; } scriptInfoMap = new HashMap<>(); scriptInfoMap.put("startScript", StartScript.class.getSimpleName()); scriptInfoMap.put("whenScript", WhenScript.class.getSimpleName()); scriptInfoMap.put("whenConditionScript", WhenConditionScript.class.getSimpleName()); scriptInfoMap.put("whenBackgroundChangesScript", WhenBackgroundChangesScript.class.getSimpleName()); scriptInfoMap.put("broadcastScript", BroadcastScript.class.getSimpleName()); scriptInfoMap.put("raspiInterruptScript", RaspiInterruptScript.class.getSimpleName()); scriptInfoMap.put("whenNfcScript", WhenNfcScript.class.getSimpleName()); scriptInfoMap.put("collisionScript", CollisionScript.class.getSimpleName()); scriptInfoMap.put("whenTouchDownScript", WhenTouchDownScript.class.getSimpleName()); } private void modifyXMLToSupportUnknownFields(File file) { initializeScriptInfoMap(); initializeBrickInfoMap(); Document originalDocument = getDocument(file); if (originalDocument != null) { updateLegoNXTFields(originalDocument); convertChildNodeToAttribute(originalDocument, "lookList", "name"); convertChildNodeToAttribute(originalDocument, "object", "name"); deleteChildNodeByName(originalDocument, "scriptList", "object"); deleteChildNodeByName(originalDocument, "brickList", "object"); deleteChildNodeByName(originalDocument.getElementsByTagName("header").item(0), "isPhiroProProject"); deleteChildNodeByName(originalDocument, "brickList", "inUserBrick"); modifyScriptLists(originalDocument); modifyBrickLists(originalDocument); modifyVariables(originalDocument); checkReferences(originalDocument.getDocumentElement()); saveDocument(originalDocument, file); } } private void updateLegoNXTFields(Document originalDocument) { final String oldDriveMotors = "MOTOR_A_C"; final String newDriveMotors = "MOTOR_B_C"; final String oldMotorMoveBrickName = "legoNxtMotorActionBrick"; final String newMotorMoveBrickName = "legoNxtMotorMoveBrick"; NodeList motors = originalDocument.getElementsByTagName("motor"); for (int i = 0; i < motors.getLength(); i++) { Node motor = motors.item(i); if (motor.getTextContent().equals(oldDriveMotors)) { motor.setTextContent(newDriveMotors); } } NodeList motorMoveBricks = originalDocument.getElementsByTagName(oldMotorMoveBrickName); for (int i = 0; i < motorMoveBricks.getLength(); i++) { Node motorMoveBrick = motorMoveBricks.item(i); originalDocument.renameNode(motorMoveBrick, motorMoveBrick.getNamespaceURI(), newMotorMoveBrickName); } } public void updateCollisionReceiverBrickMessage(File file) { final String collisionTag = "CollisionScript"; final String receivedTag = "receivedMessage"; Document originalDocument = getDocument(file); if (originalDocument != null) { NodeList scripts = originalDocument.getElementsByTagName("script"); for (int i = 0; i < scripts.getLength(); i++) { Node script = scripts.item(i); NamedNodeMap attr = script.getAttributes(); if (attr.getLength() > 0) { for (int j = 0; j < attr.getLength(); j++) { if (attr.item(j).getNodeValue().equals(collisionTag)) { NodeList messages = script.getChildNodes(); for (int k = 0; k < messages.getLength(); k++) { Node message = messages.item(k); if (message.getNodeName().equals(receivedTag)) { String broadcastMessage = message.getTextContent(); String[] broadcastMessages = broadcastMessage.split("<(\\W)*-(\\W)*>"); if (broadcastMessages[1].matches("(\\W)*ANYTHING(\\W)*")) { broadcastMessages[1] = PhysicsCollision.COLLISION_WITH_ANYTHING_IDENTIFIER; } broadcastMessage = broadcastMessages[0] + PhysicsCollision .COLLISION_MESSAGE_CONNECTOR + broadcastMessages[1]; message.setTextContent(broadcastMessage); } } } } } } } saveDocument(originalDocument, file); } private void modifyVariables(Document originalDocument) { Node variableNode = originalDocument.getElementsByTagName("variables").item(0); if (variableNode != null) { String variableNodeNamespaceURI = variableNode.getNamespaceURI(); originalDocument.renameNode(variableNode, variableNodeNamespaceURI, "data"); } else { Log.e(TAG, "XML-Update: No variables to modify."); } } private Document getDocument(File file) { try { Transformer serializer = TransformerFactory.newInstance().newTransformer(); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder docBuilder = docFactory.newDocumentBuilder(); Document doc = docBuilder.parse(file); normalizeWhiteSpaces(doc); return doc; } catch (Exception exception) { Log.e(TAG, "Failed to parse file to a Document", exception); } return null; } private void saveDocument(Document doc, File file) { try { Transformer serializer = TransformerFactory.newInstance().newTransformer(); serializer.setOutputProperty(OutputKeys.INDENT, "yes"); serializer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "2"); DOMSource source = new DOMSource(doc); StreamResult result = new StreamResult(file.getPath()); serializer.transform(source, result); } catch (Exception exception) { Log.e(TAG, "Failed to save document to file", exception); } } private void normalizeWhiteSpaces(Document document) throws XPathExpressionException { XPath xPath = XPathFactory.newInstance().newXPath(); NodeList emptyTextNodeList = (NodeList) xPath.evaluate("//text()[normalize-space(.)='']", document, XPathConstants.NODESET); for (int index = 0; index < emptyTextNodeList.getLength(); ++index) { Node emptyTextNode = emptyTextNodeList.item(index); emptyTextNode.getParentNode().removeChild(emptyTextNode); } } private Element findNodeByName(Node parentNode, String nodeName) { NodeList childNodes = parentNode.getChildNodes(); if (childNodes != null) { for (int i = 0; i < childNodes.getLength(); i++) { if (childNodes.item(i).getNodeName().equals(nodeName)) { return (Element) childNodes.item(i); } } } return null; } private void deleteChildNodeByName(Node parentNode, String childNodeName) { Node node = findNodeByName(parentNode, childNodeName); if (node != null) { parentNode.removeChild(node); } } private void deleteChildNodeByName(Document doc, String listNodeName, String childNodeName) { NodeList nodeList = doc.getElementsByTagName(listNodeName); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); if (node.hasChildNodes()) { NodeList childNodes = node.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++) { deleteChildNodeByName(childNodes.item(j), childNodeName); } } } } private void copyAttributesIfNeeded(Node sourceNode, Element destinationNode) { if (sourceNode.getNodeName().equals("loopEndlessBrick") || sourceNode.getNodeName().equals("loopEndBrick") || sourceNode.getNodeName().equals("ifLogicElseBrick") || sourceNode.getNodeName().equals("ifLogicEndBrick")) { return; } NamedNodeMap namedNodeMap = sourceNode.getAttributes(); for (int i = 0; i < namedNodeMap.getLength(); i++) { Attr node = (Attr) namedNodeMap.item(i); destinationNode.setAttributeNS(node.getNamespaceURI(), node.getName(), node.getValue()); } } private void convertChildNodeToAttribute(Document originalDocument, String parentNodeName, String childNodeName) { NodeList nodeList = originalDocument.getElementsByTagName(parentNodeName); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); Node childNode = findNodeByName(node, childNodeName); if (childNode != null && node instanceof Element) { Element elem = (Element) node; elem.setAttribute(childNodeName, childNode.getTextContent()); node.removeChild(childNode); } } } private void modifyScriptLists(Document originalDocument) { NodeList scriptListNodeList = originalDocument.getElementsByTagName("scriptList"); for (int i = 0; i < scriptListNodeList.getLength(); i++) { Node scriptListNode = scriptListNodeList.item(i); if (scriptListNode.hasChildNodes()) { NodeList scriptListChildNodes = scriptListNode.getChildNodes(); for (int j = 0; j < scriptListChildNodes.getLength(); j++) { Node scriptNode = scriptListChildNodes.item(j); Element newScriptNode = originalDocument.createElement("script"); String scriptName = scriptInfoMap.get(scriptNode.getNodeName()); if (scriptName != null) { newScriptNode.setAttribute("type", scriptName); copyAttributesIfNeeded(scriptNode, newScriptNode); if (scriptNode.hasChildNodes()) { NodeList scriptNodeChildList = scriptNode.getChildNodes(); for (int k = 0; k < scriptNodeChildList.getLength(); k++) { newScriptNode.appendChild(scriptNodeChildList.item(k)); } } scriptListNode.replaceChild(newScriptNode, scriptNode); } else { Log.e(TAG, scriptNode.getNodeName() + ": Found no scripts to convert to new structure.\""); } } } } } private void modifyBrickLists(Document originalDocument) { NodeList brickListNodeList = originalDocument.getElementsByTagName("brickList"); for (int i = 0; i < brickListNodeList.getLength(); i++) { Node brickListNode = brickListNodeList.item(i); if (brickListNode.hasChildNodes()) { NodeList brickListChildNodes = brickListNode.getChildNodes(); for (int j = 0; j < brickListChildNodes.getLength(); j++) { Node brickNode = brickListChildNodes.item(j); Element newBrickNode = originalDocument.createElement("brick"); if (brickNode.getNodeName().equals("setGhostEffectBrick")) { originalDocument.renameNode(brickNode, brickNode.getNamespaceURI(), "setTransparencyBrick"); } if (brickNode.getNodeName().equals("changeGhostEffectByNBrick")) { originalDocument.renameNode(brickNode, brickNode.getNamespaceURI(), "changeTransparencyByNBrick"); } BrickInfo brickInfo = brickInfoMap.get(brickNode.getNodeName()); if (brickInfo != null) { newBrickNode.setAttribute("type", brickInfo.brickClassName); copyAttributesIfNeeded(brickNode, newBrickNode); if (brickNode.hasChildNodes()) { NodeList brickChildNodes = brickNode.getChildNodes(); for (int k = 0; k < brickChildNodes.getLength(); k++) { Element brickChild = (Element) brickChildNodes.item(k); if (brickChild.getNodeName().equals("changeGhostEffect")) { originalDocument.renameNode(brickChild, brickChild.getNamespaceURI(), "changeTransparency"); } if (brickInfo.getBrickFieldForOldFieldName(brickChild.getNodeName()) != null) { handleFormulaNode(originalDocument, brickInfo, newBrickNode, brickChild); } else if (brickChild.getNodeName().equals("userVariable")) { handleUserVariableNode(newBrickNode, brickChild); } else if (brickChild.getNodeName().equals("loopEndBrick") || brickChild.getNodeName().equals("ifElseBrick") || brickChild.getNodeName().equals("ifEndBrick")) { continue; } else { newBrickNode.appendChild(brickChild); } } } brickListNode.replaceChild(newBrickNode, brickNode); } else { Log.e(TAG, brickNode.getNodeName() + ": Found no bricks to convert to new structure."); } } } } } private void handleFormulaNode(Document doc, BrickInfo brickInfo, Element newParentNode, Element oldNode) { Node formulaListNode = findNodeByName(newParentNode, "formulaList"); if (formulaListNode == null) { formulaListNode = doc.createElement("formulaList"); newParentNode.appendChild(formulaListNode); } Element formulaNode = findNodeByName(oldNode, "formulaTree"); if (formulaNode == null) { formulaNode = doc.createElement("formula"); } else { doc.renameNode(formulaNode, formulaNode.getNamespaceURI(), "formula"); } String category = brickInfo.getBrickFieldForOldFieldName(oldNode.getNodeName()).toString(); formulaNode.setAttribute("category", category); if (category.equals("SPEAK") || category.equals("NOTE")) { Element type = doc.createElement("type"); type.setTextContent("STRING"); formulaNode.appendChild(type); Element value = doc.createElement("value"); String textContent = oldNode.getFirstChild().getTextContent(); value.setTextContent(textContent); formulaNode.appendChild(value); } formulaListNode.appendChild(formulaNode); } private void handleUserVariableNode(Element parentNode, Element userVariableNode) { if (!userVariableNode.hasAttribute("reference")) { Node nameNode = findNodeByName(userVariableNode, "name"); if (nameNode != null) { String userVariable = nameNode.getTextContent(); userVariableNode.removeChild(nameNode); userVariableNode.setTextContent(userVariable); } } parentNode.appendChild(userVariableNode); } private void checkReferences(Element node) { if (node.hasAttribute("reference")) { node.setAttribute("reference", getValidReference(node, node.getAttribute("reference"))); } NodeList childNodes = node.getChildNodes(); if (childNodes != null) { for (int i = 0; i < childNodes.getLength(); i++) { Node childNode = childNodes.item(i); if (childNode instanceof Element) { checkReferences((Element) childNode); } } } } private String getValidReference(Node brickNode, String reference) { String[] parts = reference.split("/"); Node node = brickNode; for (int i = 0; i < parts.length; i++) { if (parts[i].equals("..")) { node = node.getParentNode(); } else { int position = 0; String nodeName = parts[i]; if (parts[i].endsWith("]")) { nodeName = parts[i].substring(0, parts[i].indexOf('[')); position = Integer.parseInt(parts[i].substring(parts[i].indexOf('[') + 1, parts[i].indexOf(']'))) - 1; } int occurrence = 0; NodeList childNodes = node.getChildNodes(); for (int j = 0; j < childNodes.getLength(); j++) { Element childNode = (Element) childNodes.item(j); if (childNode.getNodeName().equals(nodeName)) { if (occurrence == position) { node = childNode; break; } else { occurrence++; } } else if (childNode.getNodeName().equals("script") && childNode.getAttribute("type") .equals(scriptInfoMap.get(nodeName))) { if (occurrence == position) { parts[i] = "script[" + (j + 1) + "]"; node = childNode; break; } else { occurrence++; } } else if (childNode.getNodeName().equals("brick") && childNode.getAttribute("type") .equals(brickInfoMap.get(nodeName).getBrickClassName())) { if (occurrence == position) { parts[i] = "brick[" + (j + 1) + "]"; node = childNode; break; } else { occurrence++; } } } } } return generateReference(parts); } private String generateReference(String[] referenceParts) { StringBuilder builder = new StringBuilder(); for (int i = 0; i < referenceParts.length; i++) { builder.append(referenceParts[i]); if (i != referenceParts.length - 1) { builder.append('/'); } } return builder.toString(); } private class BrickInfo { private String brickClassName; private HashMap<String, BrickField> brickFieldMap; BrickInfo(String brickClassName) { this.brickClassName = brickClassName; } void addBrickFieldToMap(String oldFiledName, BrickField brickField) { if (brickFieldMap == null) { brickFieldMap = new HashMap<>(); } brickFieldMap.put(oldFiledName, brickField); } BrickField getBrickFieldForOldFieldName(String oldFiledName) { if (brickFieldMap != null) { return brickFieldMap.get(oldFiledName); } return null; } String getBrickClassName() { return brickClassName; } } }