/// <summary> /// Creates the new type of the variable based on the given parameters. /// After getting all of the correct templates, they will be copied /// into the folder specified by targetPath. /// /// Some templates must be placed in an Editor folder. If one does not /// exist, one might be created. However, this function will avoid /// overwriting files. Once it is done, it will refresh the project. /// /// Note that not all of the listen exceptions may get thrown. There /// are others which may get thrown by the file IO methods. /// </summary> /// /// <exception cref="System.ArgumentOutOfRangeException"> /// Thrown if any of the arguments are invalid. (See each argument /// for details.) /// </exception> /// /// <exception cref="System.IO.DirectoryNotFoundException"> /// If the targetPath is invalid. /// </exception> /// /// <exception cref="System.IO.IOException"> /// Thrown if the Editor folder cannot be created. Also thrown if any /// of the files we run into a pre-existing file. /// </exception> /// /// <param name="readableName"> /// The human readable name. Must be a legal C# name, and must be legal /// when used as a filename. The first character should be uppercase. /// If any of the checks fail or if the name is already taken /// by another variable object, then an ArgumentOutOfRangeException is /// thrown. /// </param> /// <param name="typeName"> /// The name of the pre-existing C# type to be represtented. If the /// string fails the checks, then an ArgumentOutOfRangeException is thrown. /// </param> /// <param name="referability"> /// Whether the C# type refered to by typeName is referable or not. /// If ReferabilityMode.Unknown is passed, ArgumentOutOfRangeException is thrown. /// </param> /// <param name="targetPath"> /// Path used when attempting to create the new files. If this isn't a /// valid path, a DirectoryNotFoundException is thrown. If the /// target folder is nested in an Editor folder (or is an Editor folder /// itself), a ArgumentOutOfRangeException is thrown instead. /// </param> public static void CreateNewVariableType( string readableName, string typeName, ReferabilityMode referability, string targetPath ) { // We'll use this to track any new files we make at any point, // which makes it easy to clean up after ourselves if things // go awry. List <string> newFilePaths = new List <string>(); string editorPath = UnityPathUtils.GetEditorFolder(targetPath); if (!IsValidName(readableName)) { throw new System.ArgumentOutOfRangeException( "readableName", "Either contains invalid characters or could conflict with" + " a C# keyword." ); } else if (ScriptFileManager.IsNameTaken(readableName)) { throw new System.ArgumentOutOfRangeException( "readableName", "Is already taken by another VariableObject type." ); } else if (!IsValidName(typeName)) { throw new System.ArgumentOutOfRangeException( "typeName", "Either contains invalid characters or could conflict with" + " a C# keyword." ); } else if (referability == ReferabilityMode.Unknown) { throw new System.ArgumentOutOfRangeException( "referability", "Must be something other than ReferabilityMode.unknown." ); } else if (!AssetDatabase.IsValidFolder(targetPath)) { throw new DirectoryNotFoundException( "targetPath must be pointing to a pre-existing folder. If" + " you want to create a new folder to put the scripts in," + " you must do it before calling CreateNewVariableType." ); } else if (UnityPathUtils.IsInEditorAssembly(targetPath)) { // If we tried putting all of our scripts in an Editor folder, // it would be mostly pointless because then we cannot use our // variable objects in the final build. throw new System.ArgumentOutOfRangeException( "targetPath", "Must not be nested in an Editor folder." ); } else if (File.Exists(editorPath) && !AssetDatabase.IsValidFolder(editorPath)) { throw new IOException( editorPath + " exists as a file, so we cannot make a folder there!" ); } // It's still possible that the editor folder doesn't exist. // However, we are sure that there isn't a file with that name, // so we can go ahead and create it if necessary. if (!AssetDatabase.IsValidFolder(editorPath)) { editorPath = AssetDatabase.GUIDToAssetPath( AssetDatabase.CreateFolder(targetPath, "Editor") ); newFilePaths.Add(editorPath); } // At this point, everything is valid. Barring some kind of error // when writting to the disk, everything should be good to go. // // The only thing we are not going to check is if the files already // exist. We could check this before-hand, but this makes it a bit // more complex overall. We don't even gain very much in doing this; // we still need to handle an exception thrown by the file-writing // code, which may require cleaning up files. // // We shall build a list of all of the files which we create. That // way, we can delete them if something goes wrong. // // // Until after the AssetDatabase.Refresh in the finally block, // we cannot rely on Unity's AssetDatabase class. This only works // on files which are part of the project. However, if start we // refreshing, it'll refresh asynchroniously. Deleting files // during a refresh is risky, and may lead to errors and even lock // up Unity's reloading mechanisms. // // Fortunately, C# has enough tools to get the job done. try { EditorApplication.LockReloadAssemblies(); foreach (TemplateInfo template in TemplateFileManager.Templates) { string newScriptPath = CreateScriptFromTemplate( readableName, typeName, referability, template, targetPath, editorPath ); // Add it to the start so that, when iterating through // later on, the new folder would be at the back. if (!string.IsNullOrEmpty(newScriptPath)) { newFilePaths.Insert(0, newScriptPath); } } // End foreach(TemplateInfo template in Files.Templates) } // End try catch (System.Exception e) { foreach (string path in newFilePaths) { if ((File.GetAttributes(path) & FileAttributes.Directory) == FileAttributes.Directory) { Directory.Delete(path); } else { File.Delete(path); } if (File.Exists(path + ".meta")) { File.Delete(path + ".meta"); } } throw e; } finally { EditorApplication.UnlockReloadAssemblies(); AssetDatabase.Refresh(); } SetFileLabels(newFilePaths.ToArray()); } // End CreateNewVariableType
private void OnWizardUpdate() { string errorMsg = ""; string helpMsg = ""; bool validName = false; bool validType = false; // We need to check for null, because the string is occasionally // null upon wizard's creation if (string.IsNullOrEmpty(humanReadableName)) { helpMsg += "Please enter the Human Readable Name."; } else if (!VariableTypeBuilder.IsValidName(humanReadableName)) { errorMsg += "The Human Readable Name is not a valid C# variable" + " name!\n"; } else if (ScriptFileManager.IsNameTaken(humanReadableName)) { errorMsg += "The Human Readable Name conflicts with another," + " already existing variable type!\n"; } else { validName = true; } helpMsg += '\n'; // Force push the next line into a blank field. if (string.IsNullOrEmpty(dataType)) { helpMsg += "Please enter the Data Type."; } else if (!VariableTypeBuilder.IsValidName(dataType)) { errorMsg += "The Data Type is not a valid C# variable name!"; } else { validType = true; } helpMsg += '\n'; // Force push the next line into a blank field. if (referability == ReferabilityMode.Unknown) { helpMsg += "Please choose a referability mode."; } // We use Trim here because having extra newlines looks ugly. errorString = errorMsg.Trim(); helpString = helpMsg; isValid = ( validName && validType && referability != ReferabilityMode.Unknown ); }