Esempio n. 1
0
        private void OnWizardCreate()
        {
            try {
                ScriptMetaData newMetaData = new ScriptMetaData(
                    humanReadableName, dataType, referability, menuOrder, builtin
                    );

                if (InEditMode)
                {
                    editTarget.RebuildFiles(newMetaData);
                }
                else
                {
                    VariableTypeBuilder.CreateNewVariableType(
                        newMetaData,
                        targetFolder.Path
                        );
                }

                // It worked, so we can go ahead and save the path now
                EditorPrefs.SetString(PathPref, targetFolder.Path);
            }
            catch (System.Exception e) {
                //Debug.LogError(e.Message);
                EditorUtility.DisplayDialog(
                    title:   "Couldn't Create Scripts!",
                    message: e.Message,
                    ok:      "Go back"
                    );

                // Re-open the menu.
                //CreateWizard(humanReadableName, dataType, referability, targetFolder.Path);
                ReopenWizard(this);
            }
        }
Esempio n. 2
0
        /// <summary>
        /// Rebuild the files associated with this type based upon its meta data.
        /// This can potentially delete scripts if the templates don't
        /// explicitly build them.
        ///
        /// Scripts are dumped into the folder pointed to by DominantPath.
        /// </summary>
        /// <param name="newMetaData">
        /// The new metadata to use. This simply means that the new scripts
        /// will use this metadata, and then we'll delete old scripts which
        /// haven't been overriden. It does NOT mean that files will get
        /// renamed.
        /// </param>
        /// <returns>The paths of the new/modified files.</returns>
        public List <string> RebuildFiles(ScriptMetaData newMetaData)
        {
            // We'll save the file paths for later... we can never really
            // trust that things won't get updated after we modify the files.
            string[] origPaths = GUIDs;
            for (int i = 0; i < origPaths.Length; i++)
            {
                origPaths[i] = UnityPathUtils.LocalizeDirectorySeparators(
                    AssetDatabase.GUIDToAssetPath(origPaths[i])
                    );
            }

            List <string> resultFiles = VariableTypeBuilder.CreateNewVariableType(
                newMetaData,
                UnityPathUtils.AbsoluteToRelative(DominantPath),
                overrideExisting: true
                );

            // Once we perform the reset, we'll want to clean up any extra files
            // which were not in our set of templates. If we find any,
            // we'll make sure to delete 'em and get everything cleaned up.
            foreach (string origPath in origPaths)
            {
                if (!resultFiles.Contains(origPath))
                {
                    AssetDatabase.DeleteAsset(origPath);
                }
            }

            return(resultFiles);
        }
Esempio n. 3
0
        /// <summary>
        /// Builds the type info, based on all of the files that match the given
        /// label. The keys are the human-readable names, NOT the type-names
        /// which the objects might handle.
        /// </summary>
        /// <returns>
        /// The type info dictionary, with the human-readable
        /// type names being the keys.
        /// </returns>
        /// <param name="label">Label which to search for.</param>
        private static Dictionary <string, ScriptSetInfo> BuildTypeInfo(string label)
        {
            Dictionary <string, ScriptSetInfo> allTypeInfo =
                new Dictionary <string, ScriptSetInfo>();

            string[] allGuids = AssetDatabase.FindAssets("l:" + label);

            foreach (string guid in allGuids)
            {
                // Used for tracking stuff we read from this specific file.
                ScriptMetaData fileData = ExtractDataFromFile(
                    AssetDatabase.GUIDToAssetPath(guid)
                    );

                // We need to see if we already know about a variable object
                // which uses this name. If not, we'll build a new typeInfo
                // object. At the end of all of this, we save the GUID of
                // the file into the typeInfo object.
                ScriptSetInfo typeInfo;
                if (!allTypeInfo.TryGetValue(fileData.name, out typeInfo))
                {
                    // First file of this typeName, so allTypeInfo doesn't
                    // contain any info on it.
                    typeInfo = new ScriptSetInfo(fileData);
                    allTypeInfo[typeInfo.Name] = typeInfo;

                    if (fileData.ParsedReferability == ReferabilityMode.Unknown)
                    {
                        Debug.LogWarning(
                            "Unable to identify the referability mode for "
                            + AssetDatabase.GUIDToAssetPath(guid)
                            );
                    }
                }                 // End if(!allTypeInfo.TryGetValue(typeName, out typeInfo))
                else
                {
                    // Only need to do these checks if there was another
                    // object which claimed this name. So there's a chance
                    // we'll see a mismatch in something.

                    if (typeInfo.MetaData != fileData)
                    {
                        Debug.LogWarning(
                            "Metadata mismatch in "
                            + AssetDatabase.GUIDToAssetPath(guid) + '\n'
                            + "Expected:\n" + typeInfo.MetaData + '\n'
                            + "But Found:\n" + fileData
                            );
                    }
                }                 // End if(!allTypeInfo.TryGetValue(typeName, out typeInfo))

                // Now we can FINALLY track this file.
                typeInfo.AddGUID(guid);
            }             // End foreach(string guid in allGuids)

            return(allTypeInfo);
        }
Esempio n. 4
0
        /// <summary>
        /// Attempts to extract the data from a given file. The data is expected
        /// to be in a JSON format, and is expected to fall between a line that
        /// contains DataHeader and another that contains DataFooter. Only the
        /// first of such blocks is heeded, and the rest are ignored.
        /// </summary>
        /// <returns>The data from the file.</returns>
        /// <param name="path">Path of file.</param>
        private static ScriptMetaData ExtractDataFromFile(string path)
        {
            StreamReader reader;

            string rawJSON    = "";
            bool   foundStart = false;
            bool   foundEnd   = false;


            reader = new StreamReader(path);

            try {
                // Read until we find the start of the data
                while (!foundStart && !reader.EndOfStream)
                {
                    string line = reader.ReadLine();
                    foundStart = line.Contains(DataHeader);
                }

                // Now read until we find the end of data or end of file
                while (!foundEnd && !reader.EndOfStream)
                {
                    string line = reader.ReadLine();

                    if (line.Contains(DataFooter))
                    {
                        foundEnd = true;
                    }
                    else
                    {
                        rawJSON += line;
                    }
                }
            }
            finally {
                reader.Close();
            }

            //UnityEngine.Debug.Log(ScriptMetaData.FromJson(rawJSON).ToJson());

            return(ScriptMetaData.FromJson(rawJSON));
        }
Esempio n. 5
0
        }         // End CreateNewVariableType

        #region File Creation
        /// <summary>
        /// Creates a script from the given template.
        ///
        /// This function does very little checking...it assumes that all names
        /// are valid C# for identifiers.
        ///
        /// Be wary that this may throw exceptions. (See StreamReader.ReadToEnd(),
        /// StreamWriter.Write(), StreamReader's contructor, and StreamWriter's
        /// constructor.)
        ///
        /// This does not issue a refresh, nor does it add any labels.
        /// </summary>
        /// <returns>
        /// The relative path (starting with the project root) to the newly
        /// created file. If it wasn't created (i.e. referability doesn't match
        /// with the types supported by the template), and empty string is
        /// returned instead.
        /// </returns>
        /// <param name="metaData">Data used to populate the template.</param>
        /// <param name="template">Info for the template.</param>
        /// <param name="normalPath">Path for non-editor scripts.</param>
        /// <param name="editorPath">Path for editor scripts.</param>
        private static string CreateScriptFromTemplate(
            ScriptMetaData metaData,
            TemplateInfo template,
            string normalPath,
            string editorPath,
            bool overrideExisting = false
            )
        {
            //string templatePath = "";   // Path of the template file
            //string newFileName = "";    // Name of the new file
            string newFilePath = "";                // Full path of new file (including name)

            // Before attempting to copy the template, we'll check if it
            // even matches what we need.
            if (template.IsCompatibleWith(metaData.ParsedReferability))
            {
                string templateName = Path.GetFileNameWithoutExtension(template.path);
                string newFileName  = metaData.ApplyReplacements(templateName) + ".cs";

                newFilePath = UnityPathUtils.Combine(
                    (template.IsEngineTemplate ? normalPath : editorPath),
                    newFileName
                    );

                if (File.Exists(newFilePath) && !overrideExisting)
                {
                    throw new IOException(newFilePath + " already exists!");
                }

                StreamReader templateReader   = null;
                string       templateContents = "";
                try {
                    // After changing the conde in this block, revise the
                    // exception documentaion above.
                    templateReader   = new StreamReader(template.path);
                    templateContents = templateReader.ReadToEnd();
                }
                finally {
                    if (templateReader != null)
                    {
                        templateReader.Close();
                    }
                }


                string newScriptContents = metaData.ApplyReplacements(templateContents);


                StreamWriter scriptWriter = null;
                try {
                    // After changing the conde in this block, revise the
                    // exception documentaion above.
                    scriptWriter = new StreamWriter(newFilePath, append: false);
                    scriptWriter.Write(newScriptContents + TemplateNewLine);

                    // Append the meta data.
                    scriptWriter.Write(ScriptSetManager.DataHeader + TemplateNewLine);
                    scriptWriter.Write(metaData.ToJson() + TemplateNewLine);
                    scriptWriter.Write(ScriptSetManager.DataFooter + TemplateNewLine);
                }
                finally {
                    if (scriptWriter != null)
                    {
                        scriptWriter.Close();
                    }
                }
            }             // End if( ... )

            return(newFilePath);
        }         // End CreateScriptFromTemplate
Esempio n. 6
0
        /// <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="metaData">
        /// All of the data to use for the new scripts.
        ///
        /// metaData.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.
        ///
        /// metaData.type: The name of the pre-existing C# type to be represtented. If
        /// the string fails the checks, then an ArgumentOutOfRangeException is thrown.
        ///
        /// metaData.ParsedReferability: Whether the C# type refered to by typeName is
        /// a class or a struct. 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>
        ///
        /// <param name="overrideExisting">
        /// If true, this will NOT check for scripts which already exist, and will
        /// happilly override whatever it finds. This will NOT change GUIDs.
        /// </param>
        ///
        /// <returns>A list of all the files created.</returns>
        public static List <string> CreateNewVariableType(
            ScriptMetaData metaData,
            string targetPath,
            bool overrideExisting = false
            )
        {
            // This is the path of the editor folder we'll use to dump our
            // editor scripts into.
            string editorPath = UnityPathUtils.GetEditorFolder(targetPath);

            #region Error checking
            if (!IsValidName(metaData.name))
            {
                throw new System.ArgumentOutOfRangeException(
                          "readableName",
                          "Either contains invalid characters or could conflict with" +
                          " a C# keyword."
                          );
            }
            else if (ScriptSetManager.IsNameTaken(metaData.name) && !overrideExisting)
            {
                throw new System.ArgumentOutOfRangeException(
                          "readableName",
                          "Is already taken by another VariableObject type."
                          );
            }
            else if (!IsValidName(metaData.type))
            {
                throw new System.ArgumentOutOfRangeException(
                          "typeName",
                          "Either contains invalid characters or could conflict with" +
                          " a C# keyword."
                          );
            }
            else if (metaData.ParsedReferability == ReferabilityMode.Unknown)
            {
                throw new System.ArgumentOutOfRangeException(
                          "referability",
                          "Must be something other than ReferabilityMode.unknown."
                          );
            }
            else if (!AssetDatabase.IsValidFolder(targetPath) && !Directory.Exists(targetPath))
            {
                // TODO This seems mildly buggy on Windows. I created a folder
                //      inside the folder-select prompt and it complained about
                //      it. Maybe, instead of using AssetDatabase, we should use
                //      normal C# checks?
                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." +
                          "(Trying to use: " + targetPath + ")"
                          );
            }
            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!"
                          );
            }
            #endregion



            /*
             * 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 (see newFIlePaths, below) of all of the
             * files which we create. That way, we can delete them if something
             * goes wrong.
             *
             *
             * Until calling AssetDatabase.Refresh in the finally block,
             * we cannot rely on Unity's AssetDatabase class without risking
             * the stability of Unity.
             *
             * If Unity begins a refresh (on its own accord), this process
             * occurs asynchroniously. In other words, it may start refreshing
             * right in the middle of this function execution.
             *
             * This is normally okay, but if we catch an exception, we need
             * to delete all of the files we created. Deleting files during a
             * refresh is risky, and may lead to errors and even lock up
             * Unity's reloading mechanisms, halting the Unity Editor.
             *
             * Fortunately, C# has enough tools to get the job done. Still,
             * it's a bit less robust than using the AssetDatabase. Also, we
             * cannot set labels until the very end of the process.
             *
             * (Again, we can't just check ahead of time. There's always risk
             * of an exception getting thrown while doing file IO.)
             */


            // Used to track the new files we add so that we can clean them up
            // if necessary. This is a stack so that the first most recent
            // things we create get deleted first.
            Stack <string> newFilePaths = new Stack <string>();

            try {
                // 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.
                //
                // Note that we're doing this BEFORE locking the assemblies so
                // we can still use the AssetDatabase class.
                if (!AssetDatabase.IsValidFolder(editorPath))
                {
                    editorPath = AssetDatabase.GUIDToAssetPath(
                        AssetDatabase.CreateFolder(targetPath, "Editor")
                        );

                    // We've created the folder, so it's one of the things
                    // we'll want to clean up if things go wrong.
                    newFilePaths.Push(editorPath);
                }


                EditorApplication.LockReloadAssemblies();

                foreach (TemplateInfo template in TemplateFileManager.Templates)
                {
                    string newScriptPath = CreateScriptFromTemplate(
                        metaData, template, targetPath, editorPath, overrideExisting
                        );

                    // CreateScriptFromTemplate CAN return an empty string if
                    // it chooses not to create a script, so we have to watch for this.
                    if (!string.IsNullOrEmpty(newScriptPath))
                    {
                        newFilePaths.Push(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);

                        // Possible Unity created a meta file already.
                        if (File.Exists(path + ".meta"))
                        {
                            File.Delete(path + ".meta");
                        }
                    }
                    else
                    {
                        File.Delete(path);
                    }
                }

                throw e;
            }
            finally {
                EditorApplication.UnlockReloadAssemblies();
                AssetDatabase.Refresh();
            }

            string label = metaData.builtin ? ScriptSetManager.UnityLabel : ScriptSetManager.CustomLabel;
            SetFileLabels(newFilePaths.ToArray(), label);

            return(new List <string>(newFilePaths));
        }         // End CreateNewVariableType
Esempio n. 7
0
 /// <summary>
 /// Creates a new script based on the given metadata.
 /// </summary>
 /// <param name="metaData">Data of the scripts.</param>
 public ScriptSetInfo(ScriptMetaData metaData)
 {
     MetaData = metaData;
     _GUIDs   = new List <string>();
 }