Below, you will find 7 healthy and cost-conscious, budget-friendly recipes, plus a shopping list for all of the ingredients. All 7 recipes are below and included in this post.
We are all about healthy cooking here. Don't get me wrong, we enjoy our treats as well. But if I can tweak something here and tweak something there to use better ingredients and healthier choices for our main entrees, I do. We also have a need to be very cost-conscious when it comes to cooking. There has to be a balance between trying to be healthy and trying to save money. Cooking healthy doesn't have to be ultra-expensive, though.
For this entree meal plan, I tried to buy the organic or more natural version of the ingredients if they were available.
Some of the ingredients are fresh vegetables, so prices will of course vary from week to week. But one week broccoli might be on sale, and the next week asparagus might be on sale, etc. It should all balance itself and stay around $70. Note: There was a sign in our aldi the last time we shopped saying something about certain vegetables being unavailable until the end of April because of weather conditions in the growing regions, so broccoli might not be available soon.
This entree meal plan has a little bit of everything. There are a couple vegetarian nights, there's a fish night, a chicken night and a beef night.
I will give you the exact ingredients and recipes OR links to recipes.
You can right click the picture below & save to your computer to print out the list.
Note: Only half of the mushrooms are used in the Skillet Chicken & Barley, so if you would like to add more veggies to the Spanish Omelette, you could slice the other half of the mushrooms and add it to the egg dish. There will also be a couple red onions left. You could add one of the colored peppers if you decide to only use one pepper in the Rainbow Veggie Quinoa. That way your Spanish Omelette would have potatoes, mushrooms, peppers, onions & sausage.
Preheat oven to 500℉. Sautee peppers and onions in olive oil over medium heat in a cast iron skillet for about 10 minutes, until onions are translucent and all veggies are soft.
Pour pineapple juice over veggies. Mix together the salt, pepper, paprika and cumin in a bowl. Place salmon fillets on top of vegetables and sprinkle with seasoning mixture.
Place cast iron skillet in oven. Cook for 10 minutes. While Salmon is cooking sautee spiralized zucchini in medium skillet for 3 - 5 minutes, until soft, but not overcooked. Add pressed garlic, salt and pepper to zucchini and stir.
Stir pineapple and cilantro together in a bowl. Place zucchini on plates or platter and top with the salmon. Spoon onions and peppers over salmon. Then, spoon on the pineapple chunks and cilantro.
Combine beef broth and quinoa in small saucepan. Bring to boil. Turn down to low and cover with tight fitting lid. Cook for 15 minutes. Turn burner off and let sit for 3 - 5 minutes. Take lid off after 5 minutes and fluff with fork. Let cool.
Bring 6 cups of water to boil in a large pot. Add the broccoli florets and cook for 3 minutes. Remove broccoli to a bowl with ice water with a slotted spoon.
Combine all of the chopped and diced veggies in a bowl. Add the blanched broccoli.
Make your dressing by whisking together the oil and seasonings.
Pour dressing over quinoa and veggies. Stir to combine.
Cover and place in fridge for at least one hour.
I used the leftover beef broth in this recipe. I know it might seem weird to combine beef broth with chicken in a dish, but sometimes I do it if I have leftover broth. It's all good. Trust me.
Cook Barley, Peas & Lentils according to package directions in beef broth and water in a medium saucepan.
While the barley is cooking, heat olive oil over medium heat in large skillet. Add onion and chicken to the skillet and sprinkle with salt and pepper. Cook until onions are translucent and chicken is no longer pink ~~about 10 minutes.
Add the mushrooms and asparagus and cook for 5 minutes longer or until asparagus and mushrooms are tender.
When barley is done, drain extra liquid and add barley to chicken and vegetables in skillet.
Add the pressed garlic and stir. Cook for 5 minutes over medium low heat.
Bring water to boil in small saucepan and add 1 cup of quick cook brown rice. Turn to low, cover with lid and cook for 5 minutes. Turn burner off and let sit for 5 minutes. Fluff with fork.
Add the drained and rinsed black beans to the brown rice and stir to combine.
Whisk together the tomato sauce, water and spices.
Put 2 heaping spoonfuls of rice and beans in the center of each tortilla. Fold the ends over on ends and then roll.
Pour sauce over enchiladas making sure that each one gets sauce over it.
Cover with lid and cook over medium/low heat for 10 minutes.
Add shredded cheese and cook for 5 minutes more.
Turn burner off and let sit for 5 minutes.
We garnished ours with green onions which we already had in the house. If you want to buy green onion, that will add around .99¢ to your total.
We used our Spanish Omelet recipe to make this oven-baked casserole. We doubled this recipe and added the breakfast sausage, which we cut into bite-sized pieces. Since the sausage is already pre-cooked, we just cut it and then added it to our egg and potato mixture before we poured it into the casserole dish. When I double the recipe, I use my 9x14 baker instead of the cast iron skillet when baking it. I place the 9x14 baker into the oven while the oven is pre-heating and then pour a little olive oil into it to coat the bottom and sides before pouring the egg/potato/sausage mixture into it. Bake at 350 for 40 - 45 minutes until the eggs are set and a toothpick or cake tester comes out clean. (The original Spanish Omelet is only 6 eggs and is cooked in a cast iron skillet, so it originally only takes around 30 minutes. When you double the recipe, it takes longer to cook. All ovens vary, so check after 35 minutes just in case) The casserole can be served warm, or can be pre-cooked and eaten out of the fridge.
This one is the most straightforward recipe on here. Cook the noodles according to package directions. We cooked ours on the low-end of the recommended time because we like our noodles a little chewy. Everyone liked these brown rice & quinoa noodles, so don't be afraid to try them! Cut the sausage into bite sized pieces and put them in a medium saucepan with the Marinara sauce. Warm the sauce until it's just bubbly and turn to low. Put drained noodles into a serving bowl and ladle Sausage Marinara sauce over the noodles.
Add all other ingredients except cheese and heat until boiling, while stirring.
Turn to low and let simmer for 30 minutes, stirring occasionally.
Garnish with cheese and/or green onions.
We served ours with cubed oven-baked hashbrown potatoes made with the leftover yellow potatoes. It was my intent to make potato wedges with the baking potatoes, but it looked like the yellow potatoes needed to be used first.
This is the first installment in a series that I might try to do creating meal plans with ingredient lists and recipes using food from aldi. Let me know if this was helpful and I'll work on creating some more complete 7 night meal plans.
Do you try to use healthier ingredients when you cook? Do you have tried and true recipes that you use on your weekly meal plan or do you like to switch it up?


Beautifully remodeled and enlarged home in well-established neighborhood. Well-designed year round sunroom (2009). Spacious 12'X18′ Master Suite addition with walk-in closet (2013). Oak hardwood floors throughout addition. First floor laundry. New roofs on house and garage, driveway replaced (2013), garage sided. New windows and side door (2016), washer and dryer (2014), refrigerator (2016), stove (2018), new storm doors front (2009). Remodeled bath with walk-in tiled shower (2009), new hot water heater (2010). Sump with battery back-up and underground utilities.


ForumCoin • View topic - Popspins-Spin & Win Hourly !(PAYING )Many payout proofs!
Popspins-Spin & Win Hourly !(PAYING )Many payout proofs!
Up to 10000 satoshi EVERY Minute!
Can you tell me more about that BTX. Cryptonia is here you exchange it? you need a wallet just for BTX?
Re: Freeth App-Earn Free ETH every 2.5 min. with this PAYING APP! NO Captcha+PROOF!
Freeth App-Earn Free ETH every 2.5 min. with this PAYING APP! NO Captcha+PROOF!
Re: Satoshiload -Win 1000/2000/5000 satoshi!
Hey, please provide more information on this. Thanks in advance.
Looks like nice. I am planning to heavily invest into monero for next year. Any idea for faucet?
Re: 5000 satoshi Sign up Bonus+500 satoshs every hour! No captcha!
5000 satoshi Sign up Bonus+500 satoshs every hour! No captcha!
Re: ONION COIN FAUCET-A new coin with very high potential!
skysnap wrote: Looks like nice. I am planning to heavily invest into monero for next year. Any idea for faucet?
Are their any Monero trading futures out there that you know of?
Re: ConnectX-FREE 50 ConnectX Coins worth $100!
ConnectX-FREE 50 ConnectX Coins worth $100!
Re: FREE ETH GIVE AWAY!-Lightchain_bot- FREE 0.001 ETH!
FREE ETH GIVE AWAY!-Lightchain_bot- FREE 0.001 ETH!
Re: Airdrop- 100 free CPS Coin!
Airdrop- 100 free CPS Coin!
Free Bitcoins • Total Clicks: 51,738 • Total Links: 38 • Add Your Link!


text:
package org.archive.crawler.writer;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.management.AttributeNotFoundException;

import org.archive.crawler.datamodel.CoreAttributeConstants;
import org.archive.crawler.datamodel.CrawlURI;
import org.archive.crawler.framework.Processor;
import org.archive.crawler.settings.ListType;
import org.archive.crawler.settings.RegularExpressionConstraint;
import org.archive.crawler.settings.SimpleType;
import org.archive.crawler.settings.StringList;
import org.archive.crawler.settings.Type;
import org.archive.io.RecordingInputStream;
import org.archive.io.ReplayInputStream;
import org.archive.net.UURI;
import org.archive.util.IoUtils;

/**
   Processor module that writes the results of successful fetches to
   files on disk.

   Writes contents of one URI to one file on disk.  The files are
   arranged in a directory hierarchy based on the URI paths.  In that sense
   they mirror the file hierarchy that might exist on the servers.

   There are a number of issues involved:

   URIs can have arbitrary length, but file systems have length constraints.

   URIs can contain characters that file systems prohibit.

   URI paths are case-sensitive, but some file systems are case-insensitive.

   This class tries very hard to map each URI into a file system path that
   obeys all file system constraints and yet reasonably represents
   the original URI.

   There would normally be a single instance of this class per Heritrix
   instance. This class is thread-safe; any number of threads can be in its
   innerProcess method at once. However, conflicts can still arise in the file
   system. For example, if several threads try to create the same directory at
   the same time, only one can win. Therefore, there should be at most one
   access to a server at a given time.

   @author Howard Lee Gayle
*/
public class MirrorWriterProcessor
extends Processor implements CoreAttributeConstants {

    private static final long serialVersionUID = 301407556928389168L;

    /**
     * Key to use asking settings for case sensitive option.
     */
    public static final String ATTR_CASE_SENSITIVE = "case-sensitive";

    /**
     * Key to use asking settings for character map.
     */
    public static final String ATTR_CHAR_MAP = "character-map";

    /**
     * Key to use asking settings for content type map.
     */
    public static final String ATTR_CONTENT_TYPE_MAP = "content-type-map";

    /**
     * Key to use asking settings for dot begin replacement.
     */
    public static final String ATTR_DOT_BEGIN = "dot-begin";

    /**
     * Key to use asking settings for dot end replacement.
     */
    public static final String ATTR_DOT_END = "dot-end";

    /**
     * Key to use asking settings for directory file.
     */
    public static final String ATTR_DIRECTORY_FILE = "directory-file";

    /**
     * Key to use asking settings for host directory option.
     */
    public static final String ATTR_HOST_DIRECTORY = "host-directory";

    /**
     * Key to use asking settings for host map.
     */
    public static final String ATTR_HOST_MAP = "host-map";

    /**
     * Key to use asking settings for maximum file system path length.
     */
    public static final String ATTR_MAX_PATH_LEN = "max-path-length";

    /**
     * Key to use asking settings for maximum file system path segment length.
     */
    public static final String ATTR_MAX_SEG_LEN = "max-segment-length";

    /**
     * Key to use asking settings for base directory path value.
     */
    public static final String ATTR_PATH = "path";

    /**
     * Key to use asking settings for port directory option.
     */
    public static final String ATTR_PORT_DIRECTORY = "port-directory";

    /**
     * Key to use asking settings for suffix at end option.
     */
    public static final String ATTR_SUFFIX_AT_END = "suffix-at-end";

    /**
     * Key to use asking settings for too-long directory.
     */
    public static final String ATTR_TOO_LONG_DIRECTORY = "too-long-directory";

    /**
     * Key to use asking settings for underscore set.
     */
    public static final String ATTR_UNDERSCORE_SET = "underscore-set";

    /** Default value for ATTR_DOT_BEGIN.*/
    private static final String DEFAULT_DOT_BEGIN = "%2E";

    /** Default maximum file system path length.*/
    private static final int DEFAULT_MAX_PATH_LEN = 1023;

    /** Default maximum file system path segment length.*/
    private static final int DEFAULT_MAX_SEG_LEN = 255;

    /** Default value for ATTR_TOO_LONG_DIRECTORY.*/
    private static final String DEFAULT_TOO_LONG_DIRECTORY = "LONG";

    /** An empty Map.*/
    private static final Map<String,String> EMPTY_MAP
     = Collections.unmodifiableMap(new TreeMap<String,String>());

    /**
       Regular expression matching a file system path segment.
       The intent is one or more non-file-separator characters.
       The backslash is to quote File.separator if it's also backslash.
    */
    private static final String PATH_SEGMENT_RE =
        "[^\\" + File.separator + "]+";

    /**
       Regular expression constraint on ATTR_DIRECTORY_FILE.
       The intent is one non-file-separator character,
       followed by zero or more characters.
       The backslash is to quote File.separator if it's also backslash.
    */
    private static final String TOO_LONG_DIRECTORY_RE =
        "[^\\" + File.separator + "].*";

    /**
     * Logger.
     */
    private static final Logger logger =
        Logger.getLogger(MirrorWriterProcessor.class.getName());

    /**
     * @param name Name of this processor.
     */
    public MirrorWriterProcessor(String name) {
        super(name, "MirrorWriter processor. " +
            "A writer that writes each URL to a file on disk named for " +
            "a derivative of the URL.");
        Type e; // Current element.
        addElementToDefinition(new SimpleType(ATTR_CASE_SENSITIVE,
            "True if the file system is case-sensitive, like UNIX. "
            + "False if the file system is case-insensitive, "
            + "like Macintosh HFS+ and Windows.",
            Boolean.TRUE));
        addElementToDefinition(new StringList(ATTR_CHAR_MAP,
            "This list is grouped in pairs. "
            + "The first string in each pair must have a length of one. "
            + "If it occurs in a URI path, "
            + "it is replaced by the second string in the pair. "
            + "For UNIX, no character mapping is normally needed. "
            + "For Macintosh, the recommended value is [: %%3A]. "
            + "For Windows, the recommended value is "
            + "[' ' %%20  " %%22  * %%2A  : %%3A  < %%3C "
            + "\\> %%3E ? %%3F  \\\\ %%5C  ^ %%5E  | %%7C]."));
        addElementToDefinition(new StringList(ATTR_CONTENT_TYPE_MAP,
            "This list is grouped in pairs. "
            + "If the content type of a resource begins (case-insensitive) "
            + "with the first string in a pair, the suffix is set to "
            + "the second string in the pair, replacing any suffix that may "
            + "have been in the URI.  For example, to force all HTML files "
            + "to have the same suffix, use [text/html html]."));
        e = addElementToDefinition(new SimpleType(ATTR_DIRECTORY_FILE,
            "Implicitly append this to a URI ending with '/'.",
            "index.html"));
        e.addConstraint(new RegularExpressionConstraint(PATH_SEGMENT_RE,
            Level.SEVERE, "This must be a simple file name."));
        e = addElementToDefinition(new SimpleType(ATTR_DOT_BEGIN,
            "If a segment starts with '.', the '.' is replaced by this.",
            DEFAULT_DOT_BEGIN));
        e.addConstraint(new RegularExpressionConstraint(PATH_SEGMENT_RE,
            Level.SEVERE,
            "This must not be empty, and must not contain " + File.separator));
        addElementToDefinition(new SimpleType(ATTR_DOT_END,
            "If a directory name ends with '.' it is replaced by this.  "
            + "For all file systems except Windows, '.' is recommended.  "
            + "For Windows, %%2E is recommended.",
            "."));
        addElementToDefinition(new StringList(ATTR_HOST_MAP,
            "This list is grouped in pairs. "
            + "If a host name matches (case-insensitive) the first string "
            + "in a pair, it is replaced by the second string in the pair.  "
            + "This can be used for consistency when several names are used "
            + "for one host, for example "
            + "[12.34.56.78 www42.foo.com]."));
        addElementToDefinition(new SimpleType(ATTR_HOST_DIRECTORY,
            "Create a subdirectory named for the host in the URI.",
            Boolean.TRUE));
        addElementToDefinition(new SimpleType(ATTR_PATH,
            "Top-level directory for mirror files.", "mirror"));

        // TODO: Add a new Constraint subclass so ATTR_MAX_PATH_LEN and
        // ATTR_MAX_SEG_LEN can be constained to reasonable values.
        addElementToDefinition(new SimpleType(ATTR_MAX_PATH_LEN,
            "Maximum file system path length.",
            new Integer(DEFAULT_MAX_PATH_LEN)));
        addElementToDefinition(new SimpleType(ATTR_MAX_SEG_LEN,
            "Maximum file system path segment length.",
            new Integer(DEFAULT_MAX_SEG_LEN)));
        addElementToDefinition(new SimpleType(ATTR_PORT_DIRECTORY,
            "Create a subdirectory named for the port in the URI.",
            Boolean.FALSE));
        addElementToDefinition(new SimpleType(ATTR_SUFFIX_AT_END,
            "If true, the suffix is placed at the end of the path, "
            + "after the query (if any).  If false, the suffix is placed "
            + "before the query.",
            Boolean.TRUE));
        e = addElementToDefinition(new SimpleType(ATTR_TOO_LONG_DIRECTORY,
            "If all the directories in the URI would exceed, "
            + "or come close to exceeding, the file system maximum "
            + "path length, then they are all replaced by this.",
            DEFAULT_TOO_LONG_DIRECTORY));
        e.addConstraint(new RegularExpressionConstraint(TOO_LONG_DIRECTORY_RE,
            Level.SEVERE, "This must be relative and not empty."));
        addElementToDefinition(new StringList(ATTR_UNDERSCORE_SET,
            "If a directory name appears (case-insensitive) in this list "
            + "then an underscore is placed before it.  "
            + "For all file systems except Windows, this is not needed.  "
            + "For Windows, the following is recommended: "
            + "[com1 com2 com3 com4 com5 com6 com7 com8 com9 "
            + "lpt1 lpt2 lpt3 lpt4 lpt5 lpt6 lpt7 lpt8 lpt9 "
            + "con nul prn]."));
    }

    protected void innerProcess(CrawlURI curi) {
        if (!curi.isSuccess()) {
            return;
        }
        UURI uuri = curi.getUURI(); // Current URI.

        // Only http and https schemes are supported.
        String scheme = uuri.getScheme();
        if (!"http".equalsIgnoreCase(scheme)
                && !"https".equalsIgnoreCase(scheme)) {
            return;
        }
        RecordingInputStream recis = curi.getHttpRecorder().getRecordedInput();
        if (0L == recis.getResponseContentLength()) {
            return;
        }

        String baseDir = null; // Base directory.
        String baseSeg = null; // ATTR_PATH value.
        try {
            baseSeg = (String) getAttribute(ATTR_PATH, curi);
        } catch (AttributeNotFoundException e) {
            logger.warning(e.getLocalizedMessage());
            return;
        }

        // Trim any trailing File.separatorChar characters from baseSeg.
        while ((baseSeg.length() > 1) && baseSeg.endsWith(File.separator)) {
            baseSeg = baseSeg.substring(0, baseSeg.length() - 1);
        }
        if (0 == baseSeg.length()) {
            baseDir = getController().getDisk().getPath();
        } else if ((new File(baseSeg)).isAbsolute()) {
            baseDir = baseSeg;
        } else {
            baseDir = getController().getDisk().getPath() + File.separator
                + baseSeg;
        }

        // Already have a path for this URI.
        boolean reCrawl = curi.containsKey(A_MIRROR_PATH);

        /*
          The file system path, relative to the value of ATTR_PATH, where
          this resource should be written.  The intent is to
          add later a persistent mapping from URI to path.
          This will allow a URI to be re-crawled and updated
          if it has changed.  If the resource has already been fetched
          and written to a file before, the path to that file
          has already been obtained from the persistent mapping
          and placed on the AList by some other module,
          such as the frontier.
        */
        String mps = null;
        File destFile = null; // Write resource contents to this file.
        try {
            if (reCrawl) {
                mps = curi.getString(A_MIRROR_PATH);
                destFile = new File(baseDir + File.separator + mps);
                File parent = destFile.getParentFile();
                if (null != parent) {
                    IoUtils.ensureWriteableDirectory(parent);
                }
            } else {
                URIToFileReturn r = null; // Return from uriToFile().
                try {
                     r = uriToFile(baseDir, curi);
                } catch (AttributeNotFoundException e) {
                    logger.warning(e.getLocalizedMessage());
                    return;
                }
                destFile = r.getFile();
                mps = r.getRelativePath();
            }
            logger.info(uuri.toString() + " -> " + destFile.getPath());
            writeToPath(recis, destFile);
            if (!reCrawl) {
                curi.putString(A_MIRROR_PATH, mps);
            }
        } catch (IOException e) {
            curi.addLocalizedError(this.getName(), e, "Mirror");
        }
    }

    /**
       Gets the directory in which the file will reside.
       Any directories needed are created.
       @param baseDir the path to the starting directory
       @param host the host part of the URI, or null if the host name
       should not be part of the returned path
       @param port the port part of the URI, or -1 if the port
       should not be part of the returned path
       @param segs all the segments in the URI
       @param maxLen the maximum path length allowed to the directory;
       this must leave some room for the file itself
       @return the directory, or null if maxLen would be exceeded
       @throws IOException
       if a needed directory could not be created
       @throws IOException
       if a needed directory is not writeable
       @throws IOException
       if a non-directory file exists with the same path as a needed directory
    */
    private URIToFileReturn dirPath(String baseDir, String host, int port,
                                    PathSegment[] segs, int maxLen)
        throws IOException {

        // Return value.
        URIToFileReturn r = new URIToFileReturn(baseDir, host, port);
        r.mkdirs();
        for (int i = 0; (segs.length - 1) != i; ++i) {
            segs[i].addToPath(r);
            if (r.longerThan(maxLen)) {
                return null;
            }
        }
        return r;
    }

    /**
       Ensures that a list contains an even number of elements.
       If not, the last element is removed.
       @param list the list
    */
    private void ensurePairs(ListType list) {
        if (1 == (list.size() % 2)) {
            list.remove(list.size() - 1);
        }
    }

    /**
       Makes a path in which a resource can be stored.
       @param baseDir the path to the starting directory
       @param curi the URI
       @return a path to the file in which to store the resource
       @throws AttributeNotFoundException
       if a needed setting is missing
       @throws IOException
       if a needed directory could not be created
       @throws IOException
       if a needed directory is not writeable
       @throws IOException
       if a non-directory file exists with the same path as a needed directory
    */
    private URIToFileReturn uriToFile(String baseDir, CrawlURI curi)
        throws AttributeNotFoundException, IOException {
        UURI uuri = curi.getUURI(); // Current URI.
        String host = null;
        Boolean hd = (Boolean) getAttribute(ATTR_HOST_DIRECTORY, curi);
        if (hd.booleanValue()) {
            host = uuri.getHost();
            StringList hostMap = (StringList) getAttribute(ATTR_HOST_MAP, curi);
            if ((null != hostMap) && (hostMap.size() > 1)) {
                ensurePairs(hostMap);
                Iterator<String> i = hostMap.typesafe().iterator();
                for (boolean more = true; more && i.hasNext();) {
                    String h1 = i.next();
                    String h2 = i.next();
                    if (host.equalsIgnoreCase(h1)) {
                        more = false;
                        if ((null != h2) && (0 != h2.length())) {
                            host = h2;
                        }
                    }
                }
            }
        }

        int port =
            ((Boolean) getAttribute(ATTR_PORT_DIRECTORY, curi)).booleanValue()
            ? uuri.getPort()
            : -1;

        String suffix = null; // Replacement suffix.
        StringList ctm = (StringList) getAttribute(ATTR_CONTENT_TYPE_MAP, curi);
        if ((null != ctm) && (ctm.size() > 1)) {
            ensurePairs(ctm);
            String contentType = curi.getContentType().toLowerCase();
            Iterator i = ctm.iterator();
            for (boolean more = true; more && i.hasNext();) {
                String ct = (String) i.next();
                String suf = (String) i.next();
                if ((null != ct) && contentType.startsWith(ct.toLowerCase())) {
                    more = false;
                    if ((null != suf) && (0 != suf.length())) {
                        suffix = suf;
                    }
                }
            }
        }

        int maxSegLen =
            ((Integer) getAttribute(ATTR_MAX_SEG_LEN, curi)).intValue();
        if (maxSegLen < 2) {
            maxSegLen = DEFAULT_MAX_SEG_LEN;
        }

        int maxPathLen =
            ((Integer) getAttribute(ATTR_MAX_PATH_LEN, curi)).intValue();
        if (maxPathLen < 2) {
            maxPathLen = DEFAULT_MAX_PATH_LEN;
        }

        Map<String,String> characterMap = EMPTY_MAP;
        StringList cm = (StringList) getAttribute(ATTR_CHAR_MAP, curi);
        if ((null != cm) && (cm.size() > 1)) {
            ensurePairs(cm);
            characterMap = new HashMap<String,String>(cm.size());
            // Above will be half full.
            for (Iterator i = cm.iterator(); i.hasNext();) {
                String s1 = (String) i.next();
                String s2 = (String) i.next();
                if ((null != s1) && (1 == s1.length()) && (null != s2)
                        && (0 != s2.length())) {
                    characterMap.put(s1, s2);
                }
            }
        }

        String dotBegin = (String) getAttribute(ATTR_DOT_BEGIN, curi);
        if (".".equals(dotBegin)) {
            dotBegin = null;
        }

        String dotEnd = (String) getAttribute(ATTR_DOT_END, curi);
        if (".".equals(dotEnd)) {
            dotEnd = null;
        }

        String tld = (String) getAttribute(ATTR_TOO_LONG_DIRECTORY, curi);
        if ((null == tld) || (0 == tld.length())
                || (-1 != tld.indexOf(File.separatorChar))) {
            tld = DEFAULT_TOO_LONG_DIRECTORY;
        }

        Set<String> underscoreSet = null;
        StringList us = (StringList) getAttribute(ATTR_UNDERSCORE_SET, curi);
        if ((null != us) && (0 != us.size())) {
            underscoreSet = new HashSet<String>(us.size(), 0.5F);
            for (String s: us.typesafe()) {
                if ((null != s) && (0 != s.length())) {
                    underscoreSet.add(s.toLowerCase());
                }
            }
        }

        return uriToFile(curi, host, port, uuri.getPath(), uuri.getQuery(),
            suffix, baseDir, maxSegLen, maxPathLen,
            ((Boolean) getAttribute(ATTR_CASE_SENSITIVE, curi)).booleanValue(),
            (String) getAttribute(ATTR_DIRECTORY_FILE, curi),
            characterMap, dotBegin, dotEnd, tld,
            ((Boolean) getAttribute(ATTR_SUFFIX_AT_END, curi)).booleanValue(),
            underscoreSet);
    }

    /**
       Makes a path in which a resource can be stored.
       @param curi the URI
       @param host the host part of the URI, or null if the host name
       should not be part of the returned path
       @param port the port part of the URI, or -1 if the port
       should not be part of the returned path
       @param uriPath the path part of the URI (must be absolute)
       @param query the query part of the URI, or null if none
       @param suffix if non-null, use this as the suffix in preference to
       any suffix that uriPath might have
       @param baseDir the path to the starting directory
       @param maxSegLen the maximum number of characters allowed in one
       file system path segment (component)
       @param maxPathLen the maximum number of characters allowed in a
       file system path
       @param caseSensitive if true, the file system is assumed to be
       case-sensitive; otherwise the file system is assumed to be
       case-insensitive but case-preserving
       @param dirFile the simple file name to append to a URI path
       ending in '/'
       @param characterMap a map from characters (as length-1 String values) in
       the URI path and query to replacement String values
       @param dotBegin if non-null, this replaces a '.' at
       the beginning of a segment
       @param dotEnd if non-null, this replaces a '.' that appears at the end
       of a directory name
       @param tooLongDir if the path length would exceed or be close to
       exceeding maxPathLen then this simple name is used as a directory
       under baseDir instead
       @param suffixAtEnd if true, the suffix is placed at the end of the
       path, after the query (if any); otherwise, the suffix is placed
       before the query
       @param underscoreSet if non-null and a segment, after conversion
       to lower case, is in this set, then prepend an underscore
       to the segment
       @return a path to the file in which to store the resource
       @throws IOException
       if a needed directory could not be created
       @throws IOException
       if a needed directory is not writeable
       @throws IOException
       if a non-directory file exists with the same path as a needed directory
    */
    private URIToFileReturn uriToFile(CrawlURI curi, String host, int port,
            String uriPath, String query, String suffix, String baseDir,
            int maxSegLen, int maxPathLen, boolean caseSensitive,
            String dirFile, Map characterMap, String dotBegin, String dotEnd,
            String tooLongDir, boolean suffixAtEnd, Set underscoreSet)
            throws IOException {
        assert (null == host) || (0 != host.length());
        assert 0 != uriPath.length();
        assert '/' == uriPath.charAt(0) : "uriPath: " + uriPath;
        assert -1 == uriPath.indexOf("//") : "uriPath: " + uriPath;
        assert -1 == uriPath.indexOf("/./") : "uriPath: " + uriPath;
        assert !uriPath.endsWith("/.") : "uriPath: " + uriPath;
        assert (null == query) || (-1 == query.indexOf('/'))
            : "query: " + query;
        assert (null == suffix)
            || ((0 != suffix.length()) && (-1 == suffix.indexOf('/')))
            : "suffix: " + suffix;
        assert 0 != baseDir.length();
        assert maxSegLen > 2 : "maxSegLen: " + maxSegLen;
        assert maxPathLen > 1;
        assert maxPathLen >= maxSegLen
            : "maxSegLen: " + maxSegLen + " maxPathLen: " + maxPathLen;
        assert 0 != dirFile.length();
        assert -1 == dirFile.indexOf("/") : "dirFile: " + dirFile;
        assert null != characterMap;
        assert (null == dotBegin) || (0 != dotBegin.length());
        assert (null == dotEnd) || !dotEnd.endsWith(".") : "dotEnd: " + dotEnd;
        assert 0 != tooLongDir.length();
        assert '/' != tooLongDir.charAt(0) : "tooLongDir: " + tooLongDir;

        int nSegs = 0; // Number of segments in the URI path.
        for (int i = 0; uriPath.length() != i; ++i) {
            if ('/' == uriPath.charAt(i)) {
                ++nSegs; // Just count slashes.
            }
        }
        assert nSegs > 0 : "uriPath: " + uriPath;
        PathSegment[] segs = new PathSegment[nSegs]; // The segments.
        int slashIndex = 0; // Index in uriPath of current /.
        for (int i = 0; (segs.length - 1) != i; ++i) {
            int nsi = uriPath.indexOf('/', slashIndex + 1); // Next index.
            assert nsi > slashIndex : "uriPath: " + uriPath;
            segs[i] = new DirSegment(uriPath, slashIndex + 1, nsi,
                                     maxSegLen, caseSensitive, curi,
                                     characterMap, dotBegin, dotEnd,
                                     underscoreSet);
            slashIndex = nsi;
        }
        if (slashIndex < (uriPath.length() - 1)) {

            // There's something after the last /.
            segs[segs.length - 1] = new EndSegment(uriPath, slashIndex + 1,
                    uriPath.length(), maxSegLen, caseSensitive, curi,
                    characterMap, dotBegin, query, suffix, maxPathLen,
                    suffixAtEnd);
        } else {

            // The URI ends with a /.
            segs[segs.length - 1] = new EndSegment(dirFile, 0, dirFile.length(),
                    maxSegLen, caseSensitive, curi, characterMap, null,
                    query, suffix, maxPathLen, suffixAtEnd);
        }
        URIToFileReturn r = dirPath(baseDir, host, port, segs,
                                    maxPathLen - maxSegLen);
        if (null == r) {

            // The path is too long.
            // Replace all the segment directories by tooLongDir.
            PathSegment endSegment = segs[segs.length - 1];
            segs = new PathSegment[2];
            segs[0] = new DirSegment(tooLongDir, 0, tooLongDir.length(),
                                     maxSegLen, caseSensitive, curi, EMPTY_MAP,
                                     null, null, null);
            segs[1] = endSegment;
            r = dirPath(baseDir, host, port, segs, maxPathLen - maxSegLen);
        }
        segs[segs.length - 1].addToPath(r);
        return r;
    }

    /**
       Copies a resource into a file.
       A temporary file is created and then atomically renamed to
       the destination file.
       This prevents leaving a partial file in case of a crash.
       @param recis the RecordingInputStream that recorded the contents
       of the resource
       @param dest the destination file
       @throws IOException on I/O error
       @throws IOException if
       the file rename fails
    */
    private void writeToPath(RecordingInputStream recis, File dest)
        throws IOException {
        ReplayInputStream replayis = recis.getContentReplayInputStream();
        File tf = new File (dest.getPath() + "N");
        FileOutputStream fos = new FileOutputStream(tf);
        try {
            replayis.readFullyTo(fos);
        } finally {
            fos.close();
            replayis.close();
        }
        if (!tf.renameTo(dest)) {
            throw new IOException("Can not rename " + tf.getAbsolutePath()
                                  + " to " + dest.getAbsolutePath());
        }

    }

    /**
       This class represents one segment (component) of a URI path.
       A segment between '/' characters is a directory segment.
       The segment after the last '/' is the end segment.
    */
    abstract class PathSegment {
        /**
           existsMaybeCaseSensitive return code
           for a file that does not exist.
        */
        protected static final int EXISTS_NOT = 1;

        /**
           existsMaybeCaseSensitive return code
           for a file that exists.
           Furthermore, the comparison is case-sensitive.
        */
        protected static final int EXISTS_EXACT_MATCH = 2;

        /**
           existsMaybeCaseSensitive return code
           for a file that exists, using a case-insensitive comparison.
           Furthermore, the file would not exist if the comparison
           were case-sensitive.
        */
        protected static final int EXISTS_CASE_INSENSITIVE_MATCH = 3;

        /** The URI, for logging and error reporting.*/
        protected CrawlURI curi;

        /**
           The main part of this segment.
           For a directory segment, that's all there is.
           For an end segment, it's the part of the URI after the last '/'
           up to but not including the '.' before the suffix (if any).
        */
        protected LumpyString mainPart = null;

        /**
           The maximum number of characters allowed
           in one file system path segment.
           A URI segment can potentially be much longer,
           but we'll trim it to this.
        */
        protected int maxSegLen;

        /** If true, the file system is assumed to be
            case-sensitive; otherwise the file system is assumed to be
            case-insensitive.
        */
        private boolean caseSensitive;

        /**
           Creates a new PathSegment.
           @param maxSegLen the maximum number of characters
           allowed in one path segment
           @param caseSensitive if true, the file system is assumed to be
           case-sensitive; otherwise the file system is assumed to be
           case-insensitive
           @param curi the URI
           @throws IllegalArgumentException if
           maxSegLen is too small
        */
        PathSegment(int maxSegLen, boolean caseSensitive, CrawlURI curi) {
            if (maxSegLen < 2) {
                throw new IllegalArgumentException("maxSegLen: " + maxSegLen);
            }
            this.maxSegLen = maxSegLen;
            this.caseSensitive = caseSensitive;
            this.curi = curi;
        }

        /**
           Adds this segment to a file path.
           This is the key method of this class.
           It extends the given path by one segment,
           named to obey all constraints.
           A new directory is created if necessary.
           @param currentPath the current path, to which this segment is added
           @throws IOException
           if a needed directory could not be created
           @throws IOException
           if a needed directory is not writeable
        */
        abstract void addToPath(URIToFileReturn currentPath) throws IOException;

        /**
           Checks if a file (including directories) exists.
           @param fsf the directory containing the file to be checked
           @param segStr the simple file or directory name
           @param check the file or directory for which to check
           @return EXISTS_NOT if check does not exist,
           EXISTS_EXACT_MATCH if check exists with a name that matches
           (case-sensitive) segStr, and
           EXISTS_CASE_INSENSITIVE_MATCH if check exists
           with a name that matches
           segStr using a case-insensitive match but not using a
           case-sensitive match
        */
        protected int existsMaybeCaseSensitive(File fsf, String segStr,
                                               File check) {
            if (caseSensitive) {
                return check.exists() ? EXISTS_EXACT_MATCH : EXISTS_NOT;
            }
            if (!check.exists()) {
                return EXISTS_NOT;
            }

            /*
              The JVM says the file exists, but the file system is assumed to be
              case-insensitive, so do we have an exact match or just a
              case-insensitive match?  We get an array of all the
              file names that match (case-insensitive) the one we're
              checking, then we can look for a case-sensitive match.
            */
            String[] fna = fsf.list(new CaseInsensitiveFilenameFilter(segStr));
            for (int i = 0; fna.length != i; ++i) {
                if (segStr.equals(fna[i])) {
                  return EXISTS_EXACT_MATCH;
                }
            }
            return EXISTS_CASE_INSENSITIVE_MATCH;
        }

        /**
           This class implements a FilenameFilter that matches
           by name, ignoring case.
        */
        class CaseInsensitiveFilenameFilter implements FilenameFilter {
            /** The file name we're looking for. */
            private String target;

            /**
               Creates a CaseInsensitiveFilenameFilter.
               @param target the target file name
               @throws IllegalArgumentException if
               target is null or empty.
            */
            CaseInsensitiveFilenameFilter(String target) {
                if (null == target) {
                    throw new IllegalArgumentException("target null");
                }
                if (0 == target.length()) {
                    throw new IllegalArgumentException("target empty");
                }
                this.target = target;
            }

            public boolean accept(File dir, String name) {
                return target.equalsIgnoreCase(name);
            }
        }
    }

    /**
       This class represents one directory segment (component) of a URI path.
    */
    class DirSegment extends PathSegment {
        /** If a segment name is in this set, prepend an underscore.*/
        private Set underscoreSet;

        /**
           Creates a DirSegment.
           @param uriPath the path part of the URI
           @param beginIndex the beginning index, inclusive, of the substring
           of uriPath to be used
           @param endIndex the ending index, exclusive, of the substring
           of uriPath to be used
           @param maxSegLen the maximum number of characters allowed in one
           file system path segment (component)
           @param caseSensitive if true, the file system is assumed to be
           case-sensitive; otherwise the file system is assumed to be
           case-insensitive but case-preserving
           @param curi the URI
           @param characterMap a map from characters
           (as length-1 String values) in
           the URI path and query to replacement String values
           @param dotBegin if non-null, this replaces a '.' at
           the beginning of the directory name
           @param dotEnd if non-null, this replaces a '.'
           that appears at the end of a directory name
           @param underscoreSet if non-null and a segment, after conversion
           to lower case, is in this set, then prepend an underscore
           to the segment
           @throws IllegalArgumentException if
           beginIndex is negative.
           @throws IllegalArgumentException if
           endIndex is less than beginIndex.
           @throws IllegalArgumentException if
           maxSegLen is too small.
        */
        DirSegment(String uriPath, int beginIndex, int endIndex, int maxSegLen,
                   boolean caseSensitive, CrawlURI curi, Map characterMap,
                   String dotBegin, String dotEnd, Set underscoreSet) {
            super(maxSegLen, caseSensitive, curi);
            mainPart = new LumpyString(uriPath, beginIndex, endIndex,
                                       (null == dotEnd) ? 0 : dotEnd.length(),
                                       this.maxSegLen, characterMap, dotBegin);
            if (null != dotEnd) {

                // We might get a segment like /VeryLong............../
                // so we have to loop to guarantee the segment doesn't
                // end with a dot.
                int dl = dotEnd.length();
                while (mainPart.endsWith('.')) {

                    // Chop off the dot at the end.
                    mainPart.trimToMax(mainPart.length() - 1);
                    if ((mainPart.length() + dl) <= this.maxSegLen) {
                        mainPart.append(dotEnd);
                    }
                }
            }
            this.underscoreSet = underscoreSet;
        }

        void addToPath(URIToFileReturn currentPath) throws IOException {
            NumberFormat nf = null;
            int startLen = mainPart.length(); // Starting length.
            for (int i = 0; ; ++i) {
                if (0 != i) {

                    // Try to create a unique file name by appending a
                    // number.
                    if (null == nf) {
                        nf = NumberFormat.getIntegerInstance();
                    }
                    String ending = nf.format(i);
                    mainPart.trimToMax(Math.min(startLen,
                                                maxSegLen - ending.length()));
                    mainPart.append(ending);
                }
                String segStr = mainPart.toString();
                if ((null != underscoreSet)
                        && underscoreSet.contains(segStr.toLowerCase())) {
                    mainPart.prepend('_');
                    ++startLen;
                    mainPart.trimToMax(maxSegLen);
                    segStr = mainPart.toString();
                }
                File fsf = currentPath.getFile();
                File f = new File(fsf, segStr);
                int er = existsMaybeCaseSensitive(fsf, segStr, f);
                switch (er) {
                case EXISTS_NOT:
                    if (!f.mkdir()) {
                        throw new IOException("Can not mkdir "
                                              + f.getAbsolutePath());
                    }
                    currentPath.append(f, segStr);
                    return; // Created new directory.

                case EXISTS_EXACT_MATCH:
                    if (f.isDirectory()) {
                        if (!f.canWrite()) {
                            throw new IOException("Directory "
                                                  + f.getAbsolutePath()
                                                  + " not writeable.");
                        }

                        /*
                          A writeable directory already exists.
                          Assume it's the one we want.
                          This assumption fails for cases like
                          http://foo.com/a*256/b.html
                          followed by
                          http://foo.com/a*256z/b.html
                          where a*256 means a sequence of the maximum allowed
                          number of "a"s.
                        */
                        currentPath.append(f, segStr);
                        return;
                    }

                    /*
                      A segment already exists but isn't a directory.
                      This could arise from, for example,
                      http://foo.com/a*256
                      followed by
                      http://foo.com/a*256b/b.html
                      We need to find a directory we created before in this
                      situation, or make a new directory with a unique name.
                      Going around the loop should eventually do that.
                    */
                    break;

                case EXISTS_CASE_INSENSITIVE_MATCH:
                    /*
                      A segment already exists that's a case-insensitive match
                      but not an exact match.  It may or may not be a directory.
                      This could arise, on a case-insensitive, case-preserving
                      file system (such as Macintosh HFS+).  For example,
                      http://foo.com/bar/z.html
                      followed by
                      http://foo.com/BAR/z.html
                      would do it.  We want bar and BAR to turn into different
                      directories.
                      Going around the loop should eventually do that.
                    */
                    break;

                default:
                    throw new IllegalStateException("Code: " + er);
                }
            }
        }
    }

    /**
       This class represents the last segment (component) of a URI path.
    */
    class EndSegment extends PathSegment {
        /**
           The number of characters in the path up to this EndSegment,
           including the final File.separatorChar.
        */
        private int dirPathLen;

        /**
           The maximum number of characters allowed in a file path, minus 1.
           The extra 1 is reserved for temporarily appending
           a character so an existing file can be replaced atomically,
           for example, by writing
           foo.htmlN
           and then renaming it to
           foo.html.
        */
        private int maxPathLen;

        /** The query part of the URI, or null if none.*/
        private LumpyString query = null;

        /**
           The suffix, or null if none.
           This isn't a LumpyString because we'd only trim a suffix
           if space were very, very tight.
        */
        private String suffix = null;

        /**
           True if the suffix goes at the end, after the query.
           False if the suffix goes before the query.
        */
        private boolean suffixAtEnd;

        /** Appended to mainPart if necessary to create a unique file name.*/
        private String uniquePart = null;

        /**
           Creates an EndSegment.
           @param uriPath the path part of the URI
           @param beginIndex the beginning index, inclusive, of the substring
           of uriPath to be used
           @param endIndex the ending index, exclusive, of the substring
           of uriPath to be used
           @param maxSegLen the maximum number of characters allowed in one
           file system path segment (component)
           @param caseSensitive if true, the file system is assumed to be
           case-sensitive; otherwise the file system is assumed to be
           case-insensitive but case-preserving
           @param curi the URI
           @param characterMap maps characters (as length-1 String values) in
           the URI path and query to replacement String values
           @param dotBegin if non-null, this replaces a '.' at
           the beginning of the segment
           @param query the query part of the URI, or null if none
           @param suffix if non-null, use this as the suffix in preference to
           any suffix that uriPath might have
           @param maxPathLen the maximum number of characters allowed in a
           file system path
           @param suffixAtEnd if true, the suffix is placed at the end of the
           path, after the query (if any); otherwise, the suffix is placed
           before the query
           @throws IllegalArgumentException if
           beginIndex is negative.
           @throws IllegalArgumentException if
           endIndex is less than beginIndex.
           @throws IllegalArgumentException if
           maxSegLen is too small.
        */
        EndSegment(String uriPath, int beginIndex, int endIndex, int maxSegLen,
                   boolean caseSensitive, CrawlURI curi, Map characterMap,
                   String dotBegin, String query, String suffix,
                   int maxPathLen, boolean suffixAtEnd) {
            super(maxSegLen - 1, caseSensitive, curi);
            int mpe = endIndex; // endIndex for the main part (no suffix).
            int ldi = uriPath.lastIndexOf('.'); // Index of last dot.
            if ((ldi > 0) && (ldi < (endIndex - 1)) && (ldi > beginIndex)) {
                mpe = ldi; // uriPath has a suffix.
            }
            this.suffix = suffix;
            if ((null == this.suffix) && (mpe < (endIndex - 1))) {

                // There's no replacement suffix and uriPath has a suffix.
                // Run it through a LumpyString to do the character mapping.
                LumpyString ls = new LumpyString(uriPath, mpe + 1, endIndex, 0,
                                                 this.maxSegLen, characterMap,
                                                 null);
                this.suffix = ls.toString();
            }
            int pad = ((null == this.suffix) ? 0 : (1 + this.suffix.length()))
                + ((null == query) ? 0 : query.length());
            mainPart = new LumpyString(uriPath, beginIndex, mpe, pad,
                                       this.maxSegLen, characterMap, dotBegin);
            this.maxPathLen = maxPathLen - 1;
            if (null != query) {
                this.query = new LumpyString(query, 0, query.length(), 0,
                                             this.maxSegLen, characterMap,
                                             null);
            }
            this.suffixAtEnd = suffixAtEnd;
        }

        void addToPath(URIToFileReturn currentPath) {
            File fsf = currentPath.getFile();
            NumberFormat nf = null;
            dirPathLen = 1 + fsf.getPath().length();
            for (int i = 0; ; ++i) {
                if (0 != i) {
                    if (null == nf) {
                        nf = NumberFormat.getIntegerInstance();
                    }
                    uniquePart = nf.format(i);
                }
                trimWithPadding((null == uniquePart) ? 0 : uniquePart.length());
                String segStr = joinParts(); // This EndSegment as a String.
                File f = new File(fsf, segStr);

                // Code for whether file exists.
                int er = existsMaybeCaseSensitive(fsf, segStr, f);
                switch (er) {
                case EXISTS_NOT:
                    currentPath.append(f, segStr);
                    return;

                case EXISTS_EXACT_MATCH:
                    if (f.isFile()) {
                        currentPath.append(f, segStr);
                        return;
                    }

                    /*
                      A file already exists but isn't an ordinary file.
                      It might be a directory, special file, named pipe,
                      whatever.
                      We need to find an unused file name,
                      or an ordinary file.
                      Going around the loop should eventually do that.
                    */
                    break;

                case EXISTS_CASE_INSENSITIVE_MATCH:
                    /*
                      A file already exists that's a case-insensitive match
                      but not an exact match.
                      This could arise, on a case-insensitive, case-preserving
                      file system (such as Macintosh HFS+).  For example,
                      http://foo.com/files.zip
                      followed by
                      http://foo.com/FILES.ZIP
                      would do it.  We want files.zip and FILES.ZIP to turn into
                      different files. Going around the loop should eventually
                      do that.
                    */
                    break;

                default:
                    throw new IllegalStateException("Code: " + er);
                }
            }
        }

        /**
           Creates a simple file name from the parts of this EndSegment.
           @return a simple file name constructed from the main part,
           unique part, query, and suffix
        */
        private String joinParts() {
            StringBuffer sb = new StringBuffer(length());
            sb.append(mainPart.asStringBuffer());
            if (null != uniquePart) {
                sb.append(uniquePart);
            }
            if (suffixAtEnd) {
                if (null != query) {
                    sb.append(query);
                }
                if (null != suffix) {
                    sb.append('.');
                    sb.append(suffix);
                }
            } else {
                if (null != suffix) {
                    sb.append('.');
                    sb.append(suffix);
                }
                if (null != query) {
                    sb.append(query);
                }
            }
            return sb.toString();
        }

        /**
           Gets the number of available character positions.
           If this EndSegment were converted to a path,
           it would have a path length and a segment length.
           There are two constraints: maxSegLen and maxPathLen.
           The number of character positions available before bumping
           into the lower constraint is computed.
           @return the number of available positions, which may be negative
        */
        private int lenAvail() {
            int len = length();
            return Math.min(maxSegLen - len, maxPathLen - dirPathLen - len);
        }

        /**
           Gets the length of the simple file name that would be
           created for this EndSegment.
           @return the length
        */
        private int length() {
            int r = mainPart.length(); // Return value.
            if (null != uniquePart) {
                r += uniquePart.length();
            }
            if (null != query) {
                r += query.length();
            }
            if (null != suffix) {
                r += 1 + suffix.length(); // 1 for the '.'
            }
            return r;
        }

        /**
           Trims this EndSegment so a given number of characters are available.
           After trimming, there will be room for at least
           padding more characters before one of the constraints is
           encountered.
           The choices for trimming, in priority order, are:

Shorten the query.

Remove the query.

Shorten the main part.

Shorten the suffix.

           @param padding the number of character positions that need to be
           available
           @throws IllegalStateException
           if it's impossible to trim enough
        */
        private void trimWithPadding(int padding) {
            assert padding >= 0 : "padding: " + padding;
            int la = lenAvail();
            if (la >= padding) {
                return;
            }

            // We need space for (padding - la) characters.
            // la might be negative.
            if (null != query) {
                query.trimToMax(Math.max(0, query.length() - (padding - la)));
                if (0 == query.length()) {
                    query = null;
                }
                la = lenAvail();
                if (la >= padding) {
                    return;
                }
            }
            mainPart.trimToMax(Math.max(1, mainPart.length() - (padding - la)));
            la = lenAvail();
            if (la >= padding) {
                return;
            }
            if (null != suffix) {
                suffix = suffix.substring(0, Math.max(1, suffix.length()
                                                      - (padding - la)));
                la = lenAvail();
                if (la >= padding) {
                    return;
                }
            }
            throw new IllegalStateException("Can not trim " + curi.toString());
        }
    }

    /**
       This class represents a dynamically growable string
       consisting of substrings ("lumps") that
       are treated atomically.  If the string is shortened, then an entire
       lump is removed.  The intent is to treat each %XX escape as a lump.
       This class also allows single characters in a source string to be
       re-mapped to a different string, possible containing more than
       one character.
       Each re-mapped character is also treated as a lump.

       For example, suppose part of a URI, between two slashes, is
       /VeryLongString...%3A/.
       We want to create a corresponding file system directory, but the string
       is a little longer than the allowed maximum.
       It's better to trim the entire
       %3A
       off the end than part of it.
       This is especially true if, later, we need to append some digits
       to create a unique directory name.
       So we treat the entire
       %3A
       as one lump.
    */
    class LumpyString {
        /**
           Lumps are indicated by an auxiliary array aux[],
           indexed the same as the string.  The LUMP_BEGIN bit is set
           for a position in the string at which a lump begins.
        */
        private static final byte LUMP_BEGIN = 0x1;

        /** Bit set for the end of a lump. */
        private static final byte LUMP_END = 0x2;

        /**
           Bit set for all characters in a lump of length greater than 1,
           except the beginning and ending characters.
        */
        private static final byte LUMP_MID = 0x4;

        /** The auxiliary array. */
        private byte[] aux;

        /** Holds the string. */
        private StringBuffer string;

        /**
           Creates a LumpyString.
           @param str the source string
           @param beginIndex the beginning index, inclusive, of the substring
           of str to be used
           @param endIndex the ending index, exclusive, of the substring
           of str to be used
           @param padding reserve this many additional character positions
           before dynamic growth is needed
           @param maxLen the maximum string length, regardless of the
           values of beginIndex, endIndex, and padding
           @param characterMap maps from characters in the source string
           (represented as length-one String values) to replacement String
           values (length at least 1).
           Each replacement string is treated as one lump.
           This is intended to cope with characters that a file system
           does not allow.
           @param dotBegin if non-null, this replaces a '.' at
           str[beginIndex]
           @throws IllegalArgumentException if
           beginIndex is negative.
           @throws IllegalArgumentException if
           endIndex is less than beginIndex.
           @throws IllegalArgumentException if
           padding is negative.
           @throws IllegalArgumentException if
           maxLen is less than one.
           @throws IllegalArgumentException if
           characterMap is null.
           @throws IllegalArgumentException if
           dotBegin is non-null but empty.
        */
        LumpyString(String str, int beginIndex, int endIndex, int padding,
                    int maxLen, Map characterMap, String dotBegin) {
            if (beginIndex < 0) {
                throw new IllegalArgumentException("beginIndex < 0: "
                                                   + beginIndex);
            }
            if (endIndex < beginIndex) {
                throw new IllegalArgumentException("endIndex < beginIndex "
                    + "beginIndex: " + beginIndex + "endIndex: " + endIndex);
            }
            if (padding < 0) {
                throw new IllegalArgumentException("padding < 0: " + padding);
            }
            if (maxLen < 1) {
                throw new IllegalArgumentException("maxLen < 1: " + maxLen);
            }
            if (null == characterMap) {
                throw new IllegalArgumentException("characterMap null");
            }
            if ((null != dotBegin) && (0 == dotBegin.length())) {
                throw new IllegalArgumentException("dotBegin empty");
            }

            // Initial capacity.  Leave some room for %XX lumps.
            // Guaranteed positive.
            int cap = Math.min(2 * (endIndex - beginIndex) + padding + 1,
                               maxLen);
            string = new StringBuffer(cap);
            aux = new byte[cap];
            for (int i = beginIndex; i != endIndex; ++i) {
                String s = str.substring(i, i + 1);
                String lump; // Next lump.
                if (".".equals(s) && (i == beginIndex) && (null != dotBegin)) {
                    lump = dotBegin;
                } else {
                    lump = (String) characterMap.get(s);
                }
                if (null == lump) {
                    if ("%".equals(s) && ((endIndex - i) > 2)
                            && (-1 != Character.digit(str.charAt(i + 1), 16))
                            && (-1 != Character.digit(str.charAt(i + 2), 16))) {

                        // %XX escape; treat as one lump.
                        lump = str.substring(i, i + 3);
                        i += 2;
                    } else {
                        lump = s;
                    }
                }
                if ((string.length() + lump.length()) > maxLen) {
                    assert checkInvariants();
                    return;
                }
                append(lump);
            }
            assert checkInvariants();
        }

        /**
           Converts this LumpyString to a String.
           @return the current string contents
        */
        public String toString() {
            assert checkInvariants();
            return string.toString();
        }

        /**
           Appends one lump to the end of this string.
           @param lump the lump (substring) to append
           @throws IllegalArgumentException if
           lump is null or empty.
        */
        void append(String lump) {
            if (null == lump) {
                throw new IllegalArgumentException("lump null");
            }
            int lumpLen = lump.length();
            if (0 == lumpLen) {
                throw new IllegalArgumentException("lump empty");
            }
            int pos = string.length(); // Current end of string.
            ensureCapacity(pos + lumpLen);
            if (1 == lumpLen) {
                aux[pos] = LUMP_BEGIN | LUMP_END;
            } else {
                assert lumpLen > 1;
                aux[pos] = LUMP_BEGIN;
                ++pos;
                for (int i = lumpLen - 2; 0 != i; --i) {
                    aux[pos] = LUMP_MID;
                    ++pos;
                }
                aux[pos] = LUMP_END;
            }
            string.append(lump);
            assert checkInvariants();
        }

        /**
           Returns the string as a StringBuffer.
           The caller should not modify the return value.
           @return the string
        */
        StringBuffer asStringBuffer() {
            return string;
        }

        /**
           Tests if this string ends with a character.
           @param ch the character to test for
           @return true if and only if this string ends with ch
        */
        boolean endsWith(char ch) {
            assert checkInvariants();
            int len = string.length();
            return (0 != len) && (string.charAt(len - 1) == ch);
        }

        /**
           Prepends one character, as a lump, to this string.
           @param ch the character to prepend
        */
        void prepend(char ch) {
            assert checkInvariants();
            int oldLen = string.length();
            ensureCapacity(1 + oldLen);
            string.insert(0, ch);
            System.arraycopy(aux, 0, aux, 1, oldLen);
            aux[0] = LUMP_BEGIN | LUMP_END;
            assert checkInvariants();
        }

        /**
           Gets the length of this string.
           @return the number of characters in this string
        */
        int length() {
            assert checkInvariants();
            return string.length();
        }

        /**
           If necessary, trims this string to a maximum length.
           Any trimming is done by removing one or more complete
           lumps from the end of this string.
           @param maxLen the new maximum length.
           After trimming, the actual length of this string will be
           at most maxLen.
           @throws IllegalArgumentException if
           maxLen is negative.
        */
        void trimToMax(int maxLen) {
            if (maxLen < 0) {
                throw new IllegalArgumentException("maxLen < 0: " + maxLen);
            }
            assert checkInvariants();
            int cl = string.length(); // Current length.
            if (cl > maxLen) {
                int nl = maxLen; // New length.
                while ((0 != nl) && (LUMP_END != (aux[nl - 1] & LUMP_END))) {
                    --nl;
                }
                for (int i = nl; i != cl; ++i) {
                    aux[i] = 0;
                }
                string.setLength(nl);
            }
            assert checkInvariants();
        }

        /**
           Checks some assertions on the instance variables.
           The intended usage is
           assert checkInvariants();
           so that if assertions are off, no call is made.
           @return true
        */
        private boolean checkInvariants() {

            // There's an aux[] element for every character in the StringBuffer.
            assert aux.length >= string.length()
                : "aux.length: " + aux.length
                + " string.length(): " + string.length();

            // The first character starts a lump.
            assert (0 == string.length())
                || (LUMP_BEGIN == (aux[0] & LUMP_BEGIN))
                : "aux[0]: " + aux[0];

            // The last character ends a lump.
            assert (0 == string.length())
                || (LUMP_END == (aux[string.length() - 1] & LUMP_END))
                : "aux[end]: " + aux[string.length() - 1];
            return true;
        }

        /**
           Ensures that the capacity is at least equal to the specified minimum.
           @param minCapacity the minimum desired capacity
        */
        private void ensureCapacity(int minCapacity) {
            assert checkInvariants();
            if (minCapacity > aux.length) {
                int nc = 2 * aux.length; // New capacity.
                while (nc < minCapacity) {
                    nc *= 2;
                }
                byte[] oldAux = aux;
                aux = new byte[nc];
                System.arraycopy(oldAux, 0, aux, 0, string.length());
            }
            string.ensureCapacity(minCapacity);
            assert checkInvariants();
        }
    }

    /**
       This class is returned by uriToFile.
       It represents a file system path, both as a File and as
       a path relative to the base directory.
    */
    class URIToFileReturn {
        /** The file system path as a File.*/
        private File filePath;

        /** The relative path from baseDir.*/
        private StringBuffer relativePath = new StringBuffer(255);

        /**
           Creates a URIToFileReturn.
           @param baseDir the path to the starting directory
           @param host the host part of the URI, or null if the host name
           should not be part of the path
           @param port the port part of the URI, or -1 if the port
           should not be part of the path
        */
        URIToFileReturn(String baseDir, String host, int port) {

            // The initial path.
            StringBuffer startPath = new StringBuffer(baseDir.length() + 32);
            startPath.append(baseDir);
            if (baseDir.endsWith(File.separator)) {
                assert 1 != baseDir.length();
                startPath.deleteCharAt(startPath.length() - 1);
            }
            if (null != host) {
                startPath.append(File.separatorChar);
                startPath.append(host);
                relativePath.append(host);
            }
            if (port > 0) {
                startPath.append(File.separatorChar);
                startPath.append(port);
                relativePath.append(File.separatorChar);
                relativePath.append(port);
            }
            filePath = new File(startPath.toString());
        }

        /**
           Appends one more segment to this path.
           @param f a File representing the path with the next segment added
           @param nextSegment the next segment
        */
        void append(File f, String nextSegment) {
            filePath = f;
            if (0 != relativePath.length()) {
                relativePath.append(File.separatorChar);
            }
            relativePath.append(nextSegment);
        }

        /**
           Gets this path as a File.
           @return this path
        */
        File getFile() {
            return filePath;
        }

        /**
           Gets this path as a relative path from the base directory.
           @return the relative path
        */
        String getRelativePath() {
            return relativePath.toString();
        }

        /**
           Tests if this path is longer than a given value.
           @param maxLen the value to test
           @return true if and only if this path is longer than maxLen
        */
        boolean longerThan(int maxLen) {
            return filePath.getPath().length() > maxLen;
        }

        /**
           Creates all directories in this path as needed.
           @throws IOException
           if a needed directory could not be created
           @throws IOException
           if a needed directory is not writeable
           @throws IOException
           if a non-directory file exists
           with the same path as a needed directory
        */
        void mkdirs() throws IOException {
            if (!filePath.exists()) {
                if (!filePath.mkdirs()) {
                    throw new IOException("Can not mkdir "
                                          + filePath.getAbsolutePath());
                }
            } else if (!filePath.canWrite()) {
                throw new IOException("Directory " + filePath.getAbsolutePath()
                                      + " not writeable.");
            } else if (!filePath.isDirectory()) {
                throw new IOException("File " + filePath.getAbsolutePath()
                                      + " is not a directory.");
            }
        }
    }
}

redpajama_set_name: RedPajamaGithub


text:
Sanity and Grace is the third live album by American singer Judy Collins, released in February 1989 by Gold Castle Records.

Overview
The album was recorded live on 10 June 1988 in Tarrytown, New York. Like its predecessors, the album is unique in that it features previously unsung songs by Collins. It is also the first full length album since 1985. The album features four Collins original songs, as well as several cover versions, including "From a Distance" and "Wind Beneath My Wings", which would go on to become big hits for Bette Midler.

Track listing

References

External links

1989 live albums
Judy Collins live albums

redpajama_set_name: RedPajamaWikipedia


mitchmcdad.com – Below we have collected Wooden Dining Chairs With Cushions that you can see and download easily. This image gallery is added by Terry Nord on March 29, 2019. We hope this Wooden Dining Chairs With Cushions gallery can give you the Best Home Design and Architecture Styles Ideas, because having a home with our own design is everyone's dream. Wooden Dining Chairs With Cushions is part of the home design plan to consider, you can make this Wooden Dining Chairs With Cushions image a reference.
Wooden Dining Chairs With Cushions – This awesome photo selection about Wooden Dining Chairs With Cushions is accessible to download. We obtain this newest image from internet and selected the greatest for you. Wooden Dining Chairs With Cushions pictures and photos selection that posted here was correctly pick and published by Terry Nord after selecting the ones which are great among the others. Did you know that Wooden Dining Chairs With Cushions has become the most attractive field in this category? This is thoroughly why we are display this field now. We took this images from the web that we feel would be seemingly the greatest representative pictures for Wooden Dining Chairs With Cushions. Many thanks for staying by here, Here is a terrific images for Wooden Dining Chairs With Cushions. We have been looking for this photos via computer network in addition to it advance separating qualified source. Provided that you are searching for any singular new idea for your home decoration then this Wooden Dining Chairs With Cushions images must be on highest of reference or you may employment it for an other plan.
Wooden Dining Chairs With Cushions is one of the pictures we found on the net from worthy of respect beginning. We settle an issue to discuss this Wooden Dining Chairs With Cushions images in this post because give approval to information from Google search engine, It is one of the top-rated demand for answer keyword on the computer network. And we also trust you arrived in this place were looking for these analysis, are not You? From abundant choices on the internet we are certain, this pictures could be the most excellent guide for you, and we honestly dream you are very happy by what we present. This images galleries has been added by Terry Nord at 2019-03-05 23:36:13 and tagged in Wooden Dining Chairs With Cushions field. And we in addition to trust it can be the most well-liked vote in google vote or occurrence in facebook share. We hope you like it as we do. Please publish and share this Wooden Dining Chairs With Cushions image for your mates, family through google plus, facebook, twitter, Instagram or another social media site.
Wooden Dining Chairs With Cushions is individual of raised place all one's own at this time. We become aware of it from search engine data such as adwords or google trends. In an effort to supply priceless info to our followers, we've attempted to discovery the nearest relevance images about Wooden Dining Chairs With Cushions. And here you will see now, this images have already been extracted from trustworthy beginning.
Wooden Dining Chairs With Cushions is a elegant Complete Home Design Ideas Collection. This Wooden Dining Chairs With Cushions was post in hope that we can contribute you an insight to Decoration your Home. This article can be your remark when you are confused to choose the right top remodeling for your sweet home. This Wooden Dining Chairs With Cushions maybe your top alternative to decor, because having a home with our own furniture styles is everyone's dream.


text:
VSM makes an impression at VIIF in Hanoi
At the Vietnam Exhibition and Fair Centre (VEFAC) in Hanoi, from 23 to 26 October 2018, VSM exhibited its abrasives for maximum stock removal and optimally ground surfaces – and was able to impress the Vietnamese industrial customers with the excellent performance of its products.
Visit VSM at METALEX 2018 in Bangkok
Around 100,000 visitors from all metal processing sectors in Thailand and Asia will come together at the end of the year in Bangkok for the METALEX, the largest trade fair for the industry in the ASEAN region. VSM will also be among the 3,300 exhibitors in 2018 and will be presenting, in addition to the popular VSM CERAMICS and COMPACTGRAIN ranges,...
A home fixture in a class of its own for VSM at EuroBLECH in Hanover
A trade fair success, product highlights for every kind of grinding task in the sheet metal working production process, and an innovation prize for VSM ILUMERON: the 25th international technology fair for sheet metal working, EuroBLECH 2018, was an all-round success for VSM.
VSM ILUMERON wins innovation award at EuroBLECH 2018 in Hanover
ILUMERON – VSM's latest product highlight – was selected by an expert panel to receive the innovation prize "Award for EuroBLECH" in the Surface Technology category. The new long-term abrasive allows grinding and polishing to be performed in one single step and produces perfect finishes with the highest level of lustre, thus combining the greatest...
VSM exhibits its new-to-market products at the 11th MetalMadrid
On 26 and 27 September, 2018, MetalMadrid, the most important industrial trade fair in central Spain, attracted the country's metal processing companies to the Spanish metropole for the 11th time. Once again VSM presented its range of abrasives as an exhibitor at the IFEMA (Feria de Madrid) exhibition site.
Sarah Trzynka
T +49 511 3526 334
sarah.trzynka@vsmabrasives.com

redpajama_set_name: RedPajamaCommonCrawl


November 2013 with only 5,000 miles on the odometer.
He just traded it back into us towards a new 2018 Denali.
and Just Serviced by our Service Department for the Next Owner.
This particular example does not show like 78,000 miles. The body is Exceptionally clean with no dings, dents, or scratches. The bumpers are not damaged. The wheels look like new. The Interior shows virtually no wear including the climate control buttons not worn or missing and the power outlet covers are not broken. There are no baby seat impressions in the leather. It Looks and Drives like a fraction of its mileage. You won't find a Cleaner Yukon anywhere!
This Denali comes complete with a its Owner's Manuals, Spare Keys, Wireless Headphones, DVD Remotes, the Original Window Sticker, and even $400 worth of New WeatherTech Floor Liners!


Where to get Hemp CBD oil, gummies and products in Fort Worth?
Where to get hemp CBD products near me in Fort Worth is a question we receive a lot. To answer this, we are building a list of the best retailers that sell hemp CBD oil products in the Fort Worth area.
Some of the local outlets are Hemp CBD shops that specialize in CBD along with retail stores that sell vape and ecig products and also carry lines of CBD products. There are also pharmacies, supplement stores as well as Fort Worth stores that carry CBD Hemp Flower.
What kind of Hemp CBD products are available in Fort Worth?
Will Pure CBD oil get me high?
No. CBD is a product that has no THC if derived from hemp and very little if derived from the cannabis plant. THC is the chemical compound that creates the "high". CBD products are available for patients that need non-euphoric treatment.
Yes, CBD for your Dogs, Cats, and pets in general is a popular treatment for caring for pets that have arthritis, chronic pain and other ailments.


I recently bought myself a Fitbit Charge which is an activity and sleep tracker. I have been researching for a few months and was thinking about waiting for Apple's watch, but my technical advisor (aka my husband) advised waiting for the next generation of the Apple watch.
It's middle of the road price wise, not the cheapest and not the most expensive either. I didn't want a pedometer as I wanted to put it on and forget about it. I wanted one where you can see the time as I didn't want to wear a watch as well as a tracker. Also I wanted to see the number of steps on the tracker rather than having to go back to the app to check how many steps I had taken.
It was easy to set up the accompanying app on my iPad and it updates as often as you like via Bluetooth which again is easy to do. All the info is displayed on a dashboard in the app and there are optional sections for you to add in how much liquid & food you have consumed. You can track your weight & exercise as well. You can view your sleep and steps by day, week, month, quarterly & year which is handy.
When you are setting up the app you select which wrist the tracker is on dominant or non-dominant. It suggests that you sleep with it on the non-dominate wrist so I swap between the two and wear it on the dominant wrist during the day.
The tracker can't tell the difference between when I'm reading my book in bed & sleeping. So it looks like I'm sleeping longer than I really am. I think the more sophisticated trackers which also pick up your heart rate are more sensitive to sleep patterns. While you don't need a tracker to tell you if you have had a good night's sleep or not, it picks up when you are restless which is useful for picking up patterns.
The step tracker is interesting. As I expected I have a days where I hit 10,000 steps by lunch time and other days not so much. I easily average 10,000 over a week.
It is an easy trigger to remind me to get out for a walk on the days where I'm not close to 10,000 steps and quite surprising the difference taking the long way home can make!
Sleep and walking are two biggies when it comes to balancing out hormones. Both are free but require time. 10,000 steps is generally the recommended target which is roughly an hour, it can be easily split up into smaller chunks if that suits your lifestyle better. A good night's sleep is usually starts by switching off anything with blue lights such as tv's or iPads. Reading is a good way to settle the brain down and therefore get to sleep quicker.
Setting goals and having an easy way to track & measure them is a cornerstone for long term success.
It has been a good purchase so far but be warned it is a little bit addictive!
As always please check with your Doctor before starting any exercise programme.