Example #1
0
        public void LoadFromAssembly(Assembly assembly, string location, Parser parser)
        {
            if (parser == null)
            {
                throw new ArgumentNullException("parser");
            }
            if (assembly == null)
            {
                throw new ArgumentNullException("assembly");
            }
            if (string.IsNullOrEmpty(location))
            {
                throw new ArgumentException("The value cannot be empty", "location");
            }

            // assembly embedded files use a dot as folder separator
            var location1 = assembly.GetName().Name + "." + location.Replace("/", ".").Replace("\\", ".");

            var traceMessage = new StringBuilder();

            // find files that match the pattern
            var files = assembly.GetManifestResourceNames()
                        .Where(x => x.StartsWith(location1, StringComparison.OrdinalIgnoreCase))
                        .ToArray();

            // determine the roles of each file
            for (int i = 0; i < files.Length; i++)
            {
                var  file      = files[i];
                bool isDefault = false;

                // decompose remaining of file name by splitting on dots
                var parts      = file.Substring(location1.Length).Split('.');
                var tags       = new List <string>();
                var dimensions = new Dictionary <string, string>(StringComparer.OrdinalIgnoreCase);
                for (int j = 0; j < parts.Length; j++)
                {
                    var part = parts[parts.Length - j - 1];

                    if (j == 0)
                    {
                        if (fileExtensions.Any(x => x.Equals(part, StringComparison.OrdinalIgnoreCase)))
                        {
                            // this .md/.txt file
                        }
                        else
                        {
                            // bad file extension
                            traceMessage.Append("File ");
                            traceMessage.Append(file);
                            traceMessage.AppendLine(" has an unknown extension.");
                            goto nextFile;
                        }
                    }
                    else if ("Default".Equals(part, StringComparison.OrdinalIgnoreCase))
                    {
                        isDefault = true;
                    }
                    else
                    {
                        var dashIndex = part.IndexOf('-');
                        if (dashIndex > 0 && dashIndex < (part.Length - 1))
                        {
                            // part is Dimension+Value
                            dimensions.Add(part.Substring(0, dashIndex), part.Substring(dashIndex + 1));
                        }
                        else
                        {
                            // part is not Dimension+Value
                            tags.Add(part);
                            traceMessage.Append("File ");
                            traceMessage.Append(file);
                            traceMessage.AppendLine(" has a file name part that does not look like a `Dimension-Value`.");
                        }
                    }
                }

                // determine standard culture
                var resource = new ResourceFile(tags);
                resource.Dimensions = dimensions;
                if (dimensions.ContainsKey("L") && dimensions.ContainsKey("R"))
                {
                    try
                    {
                        var culture = new CultureInfo(dimensions["L"] + "-" + dimensions["R"]);
                        resource.Culture = culture;
                    }
                    catch (CultureNotFoundException)
                    {
                        resource.Culture = CultureInfo.InvariantCulture;
                    }
                }
                else if (dimensions.ContainsKey("L"))
                {
                    try
                    {
                        var culture = new CultureInfo(dimensions["L"]);
                        resource.Culture = culture;
                    }
                    catch (CultureNotFoundException)
                    {
                        resource.Culture = CultureInfo.InvariantCulture;
                    }
                }

                // open and parse file
                Stream stream;
                try
                {
                    stream = assembly.GetManifestResourceStream(file);
                }
                catch (Exception ex)
                {
                    trace.Write(traceMessage);
                    throw new MarkalizeException("Failed to open file \"" + file + "\": " + ex.Message, ex);
                }

                using (var reader = new StreamReader(stream, Encoding.UTF8))
                {
                    try
                    {
                        parser.Parse(reader, resource);
                    }
                    catch (MarkalizeException ex)
                    {
                        trace.Write(traceMessage);
                        throw new MarkalizeException("Failed to parse file \"" + file + "\": " + ex.Message, ex);
                    }
                    catch (Exception ex)
                    {
                        trace.Write(traceMessage);
                        throw new MarkalizeException("Failed to parse file \"" + file + "\": " + ex.Message, ex);
                    }
                }

                if (isDefault)
                {
                    this.resources.Insert(0, resource);
                }
                else
                {
                    this.resources.Add(resource);
                }

                this.keys = null;

                nextFile :;
            }

            trace.Write(traceMessage);
        }
Example #2
0
        public void Parse(TextReader reader, ResourceFile resource)
        {
            if (reader == null)
            {
                throw new ArgumentNullException("reader");
            }

            if (resource == null)
            {
                throw new ArgumentNullException("resource");
            }

            string        line = null, nextLine = null, key = null, genre = null, lineEnding = null, nextLineEnding;
            int           skip = 0, number = 0, quoteCount = 0;
            var           prefixes = new List <string>(5);
            var           prefixesLevel = new List <byte>(5);
            bool          isInFencedCode = false, inQuotedValue = false, nextLineInContinuation = false, isVerbatim = false;
            var           lineBuilder = new StringBuilder();
            StringBuilder sb          = null;

            Func <bool> isEndOfStream;

            if (reader is StreamReader)
            {
                var streamReader = (StreamReader)reader;
                isEndOfStream = new Func <bool>(() => streamReader.EndOfStream);
            }
            else
            {
                isEndOfStream = new Func <bool>(() => reader.Peek() < 0);
            }

            // loop on all lines                     (!isEndOfStream())
            // make one extra turn for the last line (line != null))
            while (!isEndOfStream() || line != null)
            {
                if (line == null)
                {
                    // first time in loop
                    line       = string.Empty;
                    lineEnding = string.Empty;
                }

                nextLine = ReadLine(reader, out nextLineEnding);
                if (skip > 0)
                {
                    // don't do anything
                    skip--;
                }
                else if (inQuotedValue)
                {
                    var rawValue     = line;
                    var valueTrimmed = rawValue.TrimStart();
                    int leftTrim     = rawValue.Length - valueTrimmed.Length;
                    valueTrimmed = valueTrimmed.TrimEnd();
                    int endTrim = rawValue.Length - valueTrimmed.Length - leftTrim;

                    bool putLineFeed = true;
                    if (valueTrimmed[valueTrimmed.Length - 1] == '\\')
                    {
                        // line ends with \
                        valueTrimmed = valueTrimmed.Substring(0, valueTrimmed.Length - 1);
                        putLineFeed  = false;
                    }

                    if (valueTrimmed.Length > 0 && valueTrimmed[valueTrimmed.Length - 1] == '"' && valueTrimmed.Reverse().Take(quoteCount).All(c => c == '"'))
                    {
                        // line ends with '"'
                        // end of value
                        inQuotedValue = false;
                        sb.Append(valueTrimmed, 0, valueTrimmed.Length - quoteCount);
                        resource.Set(key, number, genre, sb.ToString());
                        sb = null;
                    }
                    else
                    {
                        // value continues
                        sb.Append(valueTrimmed);
                        sb.Append(rawValue, rawValue.Length - endTrim, endTrim); // cancel TrimEnd
                        sb.AppendIf(this.NormalizeCRFL(lineEnding), putLineFeed);
                    }
                }
                else if (nextLineInContinuation)
                {
                    var valueTrimmed = line.Trim();
                    if (valueTrimmed.Length > 0 && valueTrimmed[valueTrimmed.Length - 1] == '\\')
                    {
                        // line ends with '\'
                        nextLineInContinuation = true;
                        sb.Append(valueTrimmed, 0, valueTrimmed.Length - 1);
                    }
                    else
                    {
                        // end of value
                        nextLineInContinuation = false;
                        sb.Append(valueTrimmed);
                        resource.Set(key, number, genre, sb.ToString());
                        sb = null;
                    }
                }
                else if (line.StartsWith("```") || line.StartsWith("~~~"))
                {
                    if (isInFencedCode)
                    {
                        isInFencedCode = false;
                    }
                    else
                    {
                        isInFencedCode = true;
                    }
                }
                else if (string.IsNullOrEmpty(line) || isInFencedCode)
                {
                    // don't do anything
                }
                else if (IsTitle1Row(nextLine))
                {
                    // this is a title!
                    skip++;
                    var prefix = FindEnclosedPrefixInTitle(line) ?? ExtractKey(line, out string _, out int __, out string ___);
                    prefixes.Clear();
                    prefixesLevel.Clear();
                    prefixes.Add(prefix);
                    prefixesLevel.Add(1);
                }
                else if (IsTitle2Row(nextLine))
                {
                    // this is a title!
                    skip++;
                    var prefix = FindEnclosedPrefixInTitle(line) ?? ExtractKey(line, out string _, out int __, out string ___);

                    while (prefixesLevel.Count(x => x >= 2) > 0) // keep title1 prefix
                    {
                        var index = prefixes.Count - 1;
                        prefixes.RemoveAt(index);
                        prefixesLevel.RemoveAt(index);
                    }

                    prefixes.Add(prefix);
                    prefixesLevel.Add(2);
                }
                else if (IsResetTitleRow(line))
                {
                    prefixes.Clear();
                    prefixesLevel.Clear();
                }
                else
                {
                    // this line is a key+value
                    key = ExtractKey(line, out string rawValue, out number, out genre);

                    if (key != null && prefixes.Count > 0)
                    {
                        key = string.Concat(prefixes) + key;
                    }

                    if (key == null)
                    {
                        // strange text on this line
                    }
                    else if (string.IsNullOrEmpty(rawValue))
                    {
                        resource.Set(key, number, genre, rawValue);
                    }
                    else
                    {
                        var valueTrimmed = rawValue.TrimStart();
                        int leftTrim     = rawValue.Length - valueTrimmed.Length;
                        valueTrimmed = valueTrimmed.TrimEnd();
                        int  endTrim = rawValue.Length - valueTrimmed.Length - leftTrim;
                        char fc1 = '\0', fc2 = '\0', fc3 = '\0', lc1 = '\0', lc2 = '\0';
                        if (valueTrimmed.Length >= 1)
                        {
                            fc1 = valueTrimmed[0];
                            lc1 = valueTrimmed[valueTrimmed.Length - 1];
                        }

                        if (valueTrimmed.Length >= 2)
                        {
                            fc2 = valueTrimmed[1];
                            lc2 = valueTrimmed[valueTrimmed.Length - 2];
                        }

                        if (valueTrimmed.Length >= 3)
                        {
                            fc3 = valueTrimmed[2];
                        }

                        if (fc1 == '"' || (fc1 == '@' && fc2 == '"'))
                        {
                            // value starts with double quotes
                            isVerbatim   = fc1 == '@';
                            quoteCount   = valueTrimmed.SkipWhile((c, i) => c == '@' && i == 0).TakeWhile(c => c == '"').Count();
                            valueTrimmed = valueTrimmed.Substring(quoteCount + (isVerbatim ? 1 : 0));
                            leftTrim    += quoteCount + (isVerbatim ? 1 : 0);

                            if (valueTrimmed.Length > 0 && valueTrimmed[valueTrimmed.Length - 1] == '"' && valueTrimmed.Reverse().Take(quoteCount).All(c => c == '"'))
                            {
                                // value ends with double quotes
                                valueTrimmed = valueTrimmed.Substring(0, valueTrimmed.Length - quoteCount);
                                resource.Set(key, number, genre, valueTrimmed);
                            }
                            else
                            {
                                inQuotedValue = true;
                                sb            = new StringBuilder();
                                bool putLineFeed = true;
                                if (valueTrimmed[valueTrimmed.Length - 1] == '\\')
                                {
                                    // line ends with \
                                    valueTrimmed = valueTrimmed.Substring(0, valueTrimmed.Length - 1);
                                    putLineFeed  = false;
                                }

                                sb.Append(valueTrimmed);
                                sb.Append(rawValue, rawValue.Length - endTrim, endTrim); // cancel TrimEnd
                                sb.AppendIf(this.NormalizeCRFL(lineEnding), putLineFeed);

                                ////if (valueTrimmed[valueTrimmed.Length - 1] == '\\')
                                ////{
                                ////    // quoted line ends with backslash
                                ////    valueTrimmed = valueTrimmed.Remove(valueTrimmed.Length - 2);
                                ////    sb.Append(valueTrimmed);
                                ////    sb.Append(rawValue, rawValue.Length - endTrim, endTrim);
                                ////}
                                ////else
                                ////{
                                ////    sb.Append(valueTrimmed);
                                ////    sb.Append(rawValue, rawValue.Length - endTrim, endTrim);
                                ////    sb.Append(this.PreserveNewLines ? lineEnding : Environment.NewLine);
                                ////}
                            }
                        }
                        else if (valueTrimmed[valueTrimmed.Length - 1] == '\\')
                        {
                            // simple value ends with backslash
                            nextLineInContinuation = true;
                            sb = new StringBuilder();
                            sb.Append(valueTrimmed.Substring(0, valueTrimmed.Length - 1));
                        }
                        else
                        {
                            // simple value is a one-liner
                            resource.Set(key, number, genre, valueTrimmed);
                        }
                    }
                }

                line       = nextLine;
                lineEnding = nextLineEnding;
            }

            // this can't work with StreamReader: tne last line evaporates
            ////if (!string.IsNullOrEmpty(line))
            ////{
            ////    throw new MarkalizeException("File does not seem to end with an empty line");
            ////}
        }