问:
How to keep a Gridview's height the same, regardless of row numbers?
I have a common Gridview that is dynamic and has paging features. The number of rows could be anything from 0 to 'n'. When it's ZERO, I simply display a Label in the <EmptyRow> section. That is working.

I want to know how I can keep the height of the gridview set at 800px, without it stretching out the pager line.

I've tried the Height tag on the PagerStyle tag, but it makes no difference.

```<code>            <asp:GridView ID="gvFTUNSENT" runat="server"
                AutoGenerateColumns="False" CellPadding="4" ForeColor="Black" AllowSorting="True" CssClass="gvCSS" Height="800px"
                DataKeyNames="StudentID,StudentUnitID" DataSourceID="sdsFTUNSENT"
                GridLines="Vertical" AllowPaging="True" BackColor="White" BorderColor="#DEDFDE" BorderStyle="None" BorderWidth="1px" >
                <RowStyle Wrap="true" Height="48px" />
                <PagerStyle Height="20px" />
                <Columns>
</code>```

My Gridview always looks like this...

My CSS ...

```<code>table.gvCSS {
  margin-top: 2px;
  font: 12px Verdana;
}

table.gvCSS > tbody > tr {
  background-color: white;
}

table.gvCSS > tbody > tr:nth-of-type(odd) {
  background-color: #EEE;
}

table.gvCSS > tbody > tr:first-of-type {
  background-color: #5D7B9D;
  color: white;
}

table.gvCSS > tbody > tr.selected-row {
  background-color: lightgreen;
}

table.gvCSSsub > tbody > tr.selected-row {
  background-color: lightyellow;
}

table.gvCSS tr td {
  padding: 3px 6px;
}
</code>```

I want the pager line to stay at 20px, and the Gridview to "pad out" the remainding rows (if any) with blanks or empty rows.

Is that possible?

The reason is that I want the table to fit inside a table structure that is of fixed size.

Thanks

答:
I resolved it myself.

Using the power of ViewState to be precise.

First declare it..

```<code>if (!Page.IsPostBack)
{
    ViewState["DataSourceID"] = string.Empty;
}
</code>```

Add an OnLoad method..

OnLoad="GridView_Load"

```<code>    protected void GridView_Load(object sender, EventArgs e)
    {
        GridView gv = (GridView)sender;

        DataSourceSelectArguments dss = new DataSourceSelectArguments();

        //get the datasource related to the gridview
        SqlDataSource sds = (SqlDataSource)pnlMAIN.FindControl(gv.DataSourceID.ToString());
        if (sds != null)
        {
            //load the data again but this time into a dataview object
            DataView dv = (DataView)sds.Select(DataSourceSelectArguments.Empty);
            if (dv != null)
            {
                //convert the dataview to a datatable so we can see the row(s)
                DataTable dt = (DataTable)dv.ToTable();
                if (dt != null)
                {
                    //padd with blank rows to make up a full gridview of PAGESIZE (default 10) rows per page
                    if (gv.Rows.Count < gv.PageSize)
                    {
                        //and this is necessary to not stuff up gridviews with data beyond PageSize rows. Otherwise we'll have to handle the paging in code!
                        if (dt.Rows.Count <= gv.PageSize)
                        {
                            //loop through any "gap rows" and add empty rows
                            for (int wsRowAdd = (gv.Rows.Count + 1); wsRowAdd <= gv.PageSize; wsRowAdd++)
                            {
                                DataRow dr = dt.NewRow();
                                dt.Rows.Add(dr);
                            }

                            //accept the new rows
                            dt.AcceptChanges();

                            //reload the datatable back to the gridview
                            gv.DataSource = dt;
                            if (gv.DataSourceID != string.Empty)
                                ViewState["DataSourceID"] = gv.DataSourceID;
                            gv.DataSourceID = null;
                            gv.DataBind();
                        }
                    }

                    //hide gridview and reveal "emptyrow" label if there is no data to display
                    GridViewRow gHDR = (GridViewRow)gv.HeaderRow;
                    CheckBox cbHDR = (CheckBox)gHDR.FindControl("cbHDR");
                    if (gvNoData(gv))
                    {
                        gv.Visible = false;
                        Label lblEmpty = new Label();
((Label)pnlMAIN.FindControl("lblEmptyRow")).Style.Add("display", "");
                    }
                }
            }
        }
    }
</code>```

I've made a method to return true or false if the gridview has any "empty" rows...

```<code>//checks if a gridview has any actual rows of data (not just blank rows filled in by the Load below
protected bool gvNoData(GridView gv)
{
    int wsDataRow = 0;
    foreach (GridViewRow gvRow in gv.Rows)
        if (gvRow.RowType == DataControlRowType.DataRow)
        {
            Label lblStudentName = (Label)gvRow.FindControl("lblStudentName");
            if (lblStudentName != null)
                if (lblStudentName.Text.Length > 0)
                    wsDataRow += 1;
        }

    //if a count was generated then there are data rows, otherwise the rows are blank or nonexistant
    if (wsDataRow > 0) return false;
    else return true;
}
</code>```

Now the paging has to be handled by code. Here's what I did.
Note that after the page has changed, I check if it's the last page in the gridview, and apply logic to create "blank" rows of data so the gridview's HEIGHT has not changed.

```<code>protected void GridView_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
    GridView gv = (GridView)sender;
    gv.PageIndex = e.NewPageIndex;
    gv.DataBind();
}

protected void GridView_PageIndexChanged(object sender, EventArgs e)
{
    //here we control gridviews that have more than 10 rows so we can paginate and fill in rows like
    //we did above in GridView_Load(). But it could not be done there because we're adding more rows
    //to a gridview that has not had it's paging reformatted! We're doing so here

    GridView gv = (GridView)sender;

    //padd with blank rows to make up a full gridview of PAGESIZE (default 10) rows per page
    DataSourceSelectArguments dss = new DataSourceSelectArguments();

    //get the datasource related to the gridview
    string wsDataSourceID = (gv.DataSourceID == string.Empty) ? ViewState["DataSourceID"].ToString() : gv.DataSourceID;
    SqlDataSource sds = (SqlDataSource)pnlMAIN.FindControl(wsDataSourceID);
    if (sds != null)
    {
        //load the data again but this time into a dataview object
        DataView dv = (DataView)sds.Select(DataSourceSelectArguments.Empty);
        if (dv != null)
        {
            //convert the dataview to a datatable so we can see the row(s)
            DataTable dt = (DataTable)dv.ToTable();
            if (dt != null)
            {
                //have we reached the LAST page?
                if ((gv.PageIndex + 1) == ((gv.PageCount == 0) ? Convert.ToInt16(ViewState[wsDataSourceID + "PageCount"].ToString()) : gv.PageCount))
                {
                    //determines actual number of rows on the last page
                    int lastPageRowCount = dt.Rows.Count % gv.PageSize;
                    if (lastPageRowCount < gv.PageSize)
                    {
                        //loop through any "gap rows" and add empty rows
                        for (int wsRowAdd = (lastPageRowCount + 1); wsRowAdd <= gv.PageSize; wsRowAdd++)
                        {
                            DataRow dr = dt.NewRow();
                            dt.Rows.Add(dr);
                        }

                        //accept the new rows
                        dt.AcceptChanges();
                    }
                }

                //reload the datatable back to the gridview (either with extra rows, or the original data to not stuff up the paging)
                gv.DataSource = dt;
                if (gv.DataSourceID != string.Empty)
                    ViewState["DataSourceID"] = gv.DataSourceID;
                gv.DataSourceID = null;
                gv.DataBind();
            }
        }
    }
}
</code>```

Now the gotcha in all this, is that we lose the PageCount value in the gridview, so it has to be saved before it's lost. I don't know why this happens, but ASP must see that once you've generated methods to handle paging then it defaults the PageCount to 0.

So this is how I handle that...
Note that I am actually trying to handle this across 4 GridViews, so hence why I need a unique ViewState per gridview...

```<code>protected void GridView_DataBound(object sender, EventArgs e)
{
    GridView gv = (GridView)sender;

    //keep this because handing the last page and filling in rows, for some reason the PageCount is 0! And when it is, the DataSourceID could be null!
    //There should be 4 ViewStates to keep the PageCount of each GridView:
    //  1.  ViewState["sdsFTUNSENTPageCount"]
    //  2.  ViewState["sdsFTRESENDPageCount"]
    //  3.  ViewState["sdsASBAUNSENTPageCount"]
    //  4.  ViewState["sdsASBARESENDPageCount"]
    //
    //  Each one will keep a PageCount value so that we don't lose it by using PageCount alone. And this is required so that we can "padd out"
    //  EmptyRows in the gridview when it's on the last page, giving the spreadsheet that clean un-shrinking look

    if (gv.PageCount != 0)
        ViewState[((gv.DataSourceID == string.Empty) ? ViewState["DataSourceID"].ToString() : gv.DataSourceID) + "PageCount"] = gv.PageCount;
}
</code>```

And finally, I need to keep updating the ViewState of the DataSourceID, so that it can be reloaded as shown above.

I handle that here...

```<code>    protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        GridView gv = (GridView)sender;

        //update SourceID into memory, if it exists
        if (gv.DataSourceID != string.Empty)
            ViewState["DateSourceID"] = gv.DataSourceID;
}
</code>```

It's long winded, but using ViewState throughout the program I found it handy to handle "old" vs "new" values and states that I can call upon when I need to.

And so by forcing blank rows, I've kept the height of the gridview intact when paging to the LAST page and most likely there are less than the 10 rows I expected.

MS should have provided a Persistence="true" property to avoid having to do all of this. Maybe in later versions of VS this has come about, but in my case I am using VS2005 and ASP.net 2.  boo hoo.

I hope this helps someone.

Body:
I have a common Gridview that is dynamic and has paging features. The number of rows could be anything from 0 to 'n'. When it's ZERO, I simply display a Label in the <EmptyRow> section. That is working.

I want to know how I can keep the height of the gridview set at 800px, without it stretching out the pager line.

I've tried the Height tag on the PagerStyle tag, but it makes no difference.

            <asp:GridView ID="gvFTUNSENT" runat="server"
                AutoGenerateColumns="False" CellPadding="4" ForeColor="Black" AllowSorting="True" CssClass="gvCSS" Height="800px"
                DataKeyNames="StudentID,StudentUnitID" DataSourceID="sdsFTUNSENT"
                GridLines="Vertical" AllowPaging="True" BackColor="White" BorderColor="#DEDFDE" BorderStyle="None" BorderWidth="1px" >
                <RowStyle Wrap="true" Height="48px" />
                <PagerStyle Height="20px" />
                <Columns>

My Gridview always looks like this...

My CSS ...

table.gvCSS {
  margin-top: 2px;
  font: 12px Verdana;
}

table.gvCSS > tbody > tr {
  background-color: white;
}

table.gvCSS > tbody > tr:nth-of-type(odd) {
  background-color: #EEE;
}

table.gvCSS > tbody > tr:first-of-type {
  background-color: #5D7B9D;
  color: white;
}

table.gvCSS > tbody > tr.selected-row {
  background-color: lightgreen;
}

table.gvCSSsub > tbody > tr.selected-row {
  background-color: lightyellow;
}

table.gvCSS tr td {
  padding: 3px 6px;
}

I want the pager line to stay at 20px, and the Gridview to "pad out" the remainding rows (if any) with blanks or empty rows.

Is that possible?

The reason is that I want the table to fit inside a table structure that is of fixed size.

Thanks

Body:
I resolved it myself.

Using the power of ViewState to be precise.

First declare it..

if (!Page.IsPostBack)
{
    ViewState["DataSourceID"] = string.Empty;
}

Add an OnLoad method..

OnLoad="GridView_Load"

    protected void GridView_Load(object sender, EventArgs e)
    {
        GridView gv = (GridView)sender;

        DataSourceSelectArguments dss = new DataSourceSelectArguments();

        //get the datasource related to the gridview
        SqlDataSource sds = (SqlDataSource)pnlMAIN.FindControl(gv.DataSourceID.ToString());
        if (sds != null)
        {
            //load the data again but this time into a dataview object
            DataView dv = (DataView)sds.Select(DataSourceSelectArguments.Empty);
            if (dv != null)
            {
                //convert the dataview to a datatable so we can see the row(s)
                DataTable dt = (DataTable)dv.ToTable();
                if (dt != null)
                {
                    //padd with blank rows to make up a full gridview of PAGESIZE (default 10) rows per page
                    if (gv.Rows.Count < gv.PageSize)
                    {
                        //and this is necessary to not stuff up gridviews with data beyond PageSize rows. Otherwise we'll have to handle the paging in code!
                        if (dt.Rows.Count <= gv.PageSize)
                        {
                            //loop through any "gap rows" and add empty rows
                            for (int wsRowAdd = (gv.Rows.Count + 1); wsRowAdd <= gv.PageSize; wsRowAdd++)
                            {
                                DataRow dr = dt.NewRow();
                                dt.Rows.Add(dr);
                            }

                            //accept the new rows
                            dt.AcceptChanges();

                            //reload the datatable back to the gridview
                            gv.DataSource = dt;
                            if (gv.DataSourceID != string.Empty)
                                ViewState["DataSourceID"] = gv.DataSourceID;
                            gv.DataSourceID = null;
                            gv.DataBind();
                        }
                    }

                    //hide gridview and reveal "emptyrow" label if there is no data to display
                    GridViewRow gHDR = (GridViewRow)gv.HeaderRow;
                    CheckBox cbHDR = (CheckBox)gHDR.FindControl("cbHDR");
                    if (gvNoData(gv))
                    {
                        gv.Visible = false;
                        Label lblEmpty = new Label();
((Label)pnlMAIN.FindControl("lblEmptyRow")).Style.Add("display", "");
                    }
                }
            }
        }
    }

I've made a method to return true or false if the gridview has any "empty" rows...

//checks if a gridview has any actual rows of data (not just blank rows filled in by the Load below
protected bool gvNoData(GridView gv)
{
    int wsDataRow = 0;
    foreach (GridViewRow gvRow in gv.Rows)
        if (gvRow.RowType == DataControlRowType.DataRow)
        {
            Label lblStudentName = (Label)gvRow.FindControl("lblStudentName");
            if (lblStudentName != null)
                if (lblStudentName.Text.Length > 0)
                    wsDataRow += 1;
        }

    //if a count was generated then there are data rows, otherwise the rows are blank or nonexistant
    if (wsDataRow > 0) return false;
    else return true;
}

Now the paging has to be handled by code. Here's what I did.
Note that after the page has changed, I check if it's the last page in the gridview, and apply logic to create "blank" rows of data so the gridview's HEIGHT has not changed.

protected void GridView_PageIndexChanging(object sender, GridViewPageEventArgs e)
{
    GridView gv = (GridView)sender;
    gv.PageIndex = e.NewPageIndex;
    gv.DataBind();
}

protected void GridView_PageIndexChanged(object sender, EventArgs e)
{
    //here we control gridviews that have more than 10 rows so we can paginate and fill in rows like
    //we did above in GridView_Load(). But it could not be done there because we're adding more rows
    //to a gridview that has not had it's paging reformatted! We're doing so here

    GridView gv = (GridView)sender;

    //padd with blank rows to make up a full gridview of PAGESIZE (default 10) rows per page
    DataSourceSelectArguments dss = new DataSourceSelectArguments();

    //get the datasource related to the gridview
    string wsDataSourceID = (gv.DataSourceID == string.Empty) ? ViewState["DataSourceID"].ToString() : gv.DataSourceID;
    SqlDataSource sds = (SqlDataSource)pnlMAIN.FindControl(wsDataSourceID);
    if (sds != null)
    {
        //load the data again but this time into a dataview object
        DataView dv = (DataView)sds.Select(DataSourceSelectArguments.Empty);
        if (dv != null)
        {
            //convert the dataview to a datatable so we can see the row(s)
            DataTable dt = (DataTable)dv.ToTable();
            if (dt != null)
            {
                //have we reached the LAST page?
                if ((gv.PageIndex + 1) == ((gv.PageCount == 0) ? Convert.ToInt16(ViewState[wsDataSourceID + "PageCount"].ToString()) : gv.PageCount))
                {
                    //determines actual number of rows on the last page
                    int lastPageRowCount = dt.Rows.Count % gv.PageSize;
                    if (lastPageRowCount < gv.PageSize)
                    {
                        //loop through any "gap rows" and add empty rows
                        for (int wsRowAdd = (lastPageRowCount + 1); wsRowAdd <= gv.PageSize; wsRowAdd++)
                        {
                            DataRow dr = dt.NewRow();
                            dt.Rows.Add(dr);
                        }

                        //accept the new rows
                        dt.AcceptChanges();
                    }
                }

                //reload the datatable back to the gridview (either with extra rows, or the original data to not stuff up the paging)
                gv.DataSource = dt;
                if (gv.DataSourceID != string.Empty)
                    ViewState["DataSourceID"] = gv.DataSourceID;
                gv.DataSourceID = null;
                gv.DataBind();
            }
        }
    }
}

Now the gotcha in all this, is that we lose the PageCount value in the gridview, so it has to be saved before it's lost. I don't know why this happens, but ASP must see that once you've generated methods to handle paging then it defaults the PageCount to 0.

So this is how I handle that...
Note that I am actually trying to handle this across 4 GridViews, so hence why I need a unique ViewState per gridview...

protected void GridView_DataBound(object sender, EventArgs e)
{
    GridView gv = (GridView)sender;

    //keep this because handing the last page and filling in rows, for some reason the PageCount is 0! And when it is, the DataSourceID could be null!
    //There should be 4 ViewStates to keep the PageCount of each GridView:
    //  1.  ViewState["sdsFTUNSENTPageCount"]
    //  2.  ViewState["sdsFTRESENDPageCount"]
    //  3.  ViewState["sdsASBAUNSENTPageCount"]
    //  4.  ViewState["sdsASBARESENDPageCount"]
    //
    //  Each one will keep a PageCount value so that we don't lose it by using PageCount alone. And this is required so that we can "padd out"
    //  EmptyRows in the gridview when it's on the last page, giving the spreadsheet that clean un-shrinking look

    if (gv.PageCount != 0)
        ViewState[((gv.DataSourceID == string.Empty) ? ViewState["DataSourceID"].ToString() : gv.DataSourceID) + "PageCount"] = gv.PageCount;
}

And finally, I need to keep updating the ViewState of the DataSourceID, so that it can be reloaded as shown above.

I handle that here...

    protected void GridView_RowDataBound(object sender, GridViewRowEventArgs e)
    {
        GridView gv = (GridView)sender;

        //update SourceID into memory, if it exists
        if (gv.DataSourceID != string.Empty)
            ViewState["DateSourceID"] = gv.DataSourceID;
}

It's long winded, but using ViewState throughout the program I found it handy to handle "old" vs "new" values and states that I can call upon when I need to.

And so by forcing blank rows, I've kept the height of the gridview intact when paging to the LAST page and most likely there are less than the 10 rows I expected.

MS should have provided a Persistence="true" property to avoid having to do all of this. Maybe in later versions of VS this has come about, but in my case I am using VS2005 and ASP.net 2.  boo hoo.

I hope this helps someone.


来源: stackexchange

问:
Link not working on image button
I have image buttons that change appearance on hover by changing the content of the image in CSS. Not sure if that is part of the problem but for some reason when I click on my image link nothing happens.

Here's the code I am using:

```<code><div class="hub-background">
    <div class="hub-container">
        <article class="btn-twitch">
            <a href="twitch.tv/destifly">;
            <img src=" ***** "> </a>
        </article>
    </div>
</div>
</code>```

答:
You need to put the article inside of the anchor tag</p>

```<code><div class="hub-background">
  <div class="hub-container">
    <a href="...">
      <article class="btn-twitch">
        <img src="...">
      </article>
    </a>
  </div>
</div>
</code>```

ContentLicense: CC BY-SA 3.0

Title: Link not working on image button

Body:
I have image buttons that change appearance on hover by changing the content of the image in CSS. Not sure if that is part of the problem but for some reason when I click on my image link nothing happens.

Here's the code I am using:

            ;

Body:
You need to put the article inside of the anchor tag

<div class="hub-background">
  <div class="hub-container">
    <a href="...">
      <article class="btn-twitch">
        <img src="...">
      </article>
    </a>
  </div>
</div>


问:
Image max-width not working in media query
I'm having an issue with max-width/max-height inside of media queries.  Basically what's happening is I have an image gallery that uses jQuery to display a modal window. This modal window consists of the modal, a content box, and an image viewer which has some buttons inside for navigation thru images). I have the max-width/max-height set in vw and vh in css but the exit button gets cut off on a mobile device.

Current CSS:

```<code>#photoViewer {
display: inline-block;
position: relative;
margin: 0 auto;
width: 0;
height: 0;
transition: width 0.5s, height 0.5s;
}

#photoViewer img {
position: absolute;
top: 50%;
left: 50%;
max-height: 70vh;
max-width: 70vw;
}
</code>```

I tried using a media query:

```<code>@media screen and (max-width: 955px) {
   #photoViewer img {
      max-width: 50vw;
      max-height: 50vh;
   }
}
</code>```

but it's not changing anything. I found if set hard "width" and "height" rules it works but the max- seems to throw it off.  Any ideas?

Here's a fiddle that demonstrates kind of what I'm working with (my whole code is pretty large):

https://jsfiddle.net/jessereitz1/6nxg21a3/3/

答: Thanks everyone for your help and insight. I just found the biggest bug anyone can find in their code: human error and stupidity. I found I had placed an identical CSS rule at the bottom of my code setting the img max-height/width to 90vh/vw. I can now say I've learned to scroll through ALL my code before hopping on here! Thanks again!</p>

Body:
I'm having an issue with max-width/max-height inside of media queries.  Basically what's happening is I have an image gallery that uses jQuery to display a modal window. This modal window consists of the modal, a content box, and an image viewer which has some buttons inside for navigation thru images). I have the max-width/max-height set in vw and vh in css but the exit button gets cut off on a mobile device.

Current CSS:

#photoViewer {
display: inline-block;
position: relative;
margin: 0 auto;
width: 0;
height: 0;
transition: width 0.5s, height 0.5s;
}

#photoViewer img {
position: absolute;
top: 50%;
left: 50%;
max-height: 70vh;
max-width: 70vw;
}

I tried using a media query:

@media screen and (max-width: 955px) {
   #photoViewer img {
      max-width: 50vw;
      max-height: 50vh;
   }
}

but it's not changing anything. I found if set hard "width" and "height" rules it works but the max- seems to throw it off.  Any ideas?

Here's a fiddle that demonstrates kind of what I'm working with (my whole code is pretty large):

https://jsfiddle.net/jessereitz1/6nxg21a3/3/


问:
Detecting change in website Python3
I am taking data from a webpage that updates every morning that updates at different times and I would like to know how to get a script to run every 10 minutes or so to check if the website has been updated. I was thinking of somehow using cron but I don't understand it very well. Thanks for your help.</p>

答:
Have you tried using the package APScheduler? It makes it fairly simple to schedule tasks. Here's the documentation.

To do a scheduled task, this is all that needs to be done (for something basic):

```<code>from apscheduler.schedulers.background import BackgroundScheduler
from pytz import utc

scheduler = BackgroundScheduler()
scheduler.configure(timezone=utc)

def print_hello():
    print("Hello, this is a scheduled event!")

job = scheduler.add_job(print_hello, 'interval', minutes=1, max_instances=10)
scheduler.start()
</code>```

Note, however, that I had a small bug when I first tried to use the library, but an explanation of how to fix that is here.

Title: Detecting change in website Python3

Body:
Have you tried using the package APScheduler? It makes it fairly simple to schedule tasks. Here's the documentation.

To do a scheduled task, this is all that needs to be done (for something basic):

from apscheduler.schedulers.background import BackgroundScheduler
from pytz import utc

scheduler = BackgroundScheduler()
scheduler.configure(timezone=utc)

def print_hello():
    print("Hello, this is a scheduled event!")

job = scheduler.add_job(print_hello, 'interval', minutes=1, max_instances=10)
scheduler.start()

Note, however, that I had a small bug when I first tried to use the library, but an explanation of how to fix that is here.


问:
Generating multiple flowfiles using the Nifi ExecuteScript processor
I am working on a Nifi flow where I am getting a JSON document with multiple key-value pairs. I am using the ExecuteScript processor with python.

My goal here is to create various URLS bases on JSON keys. The keys are numerical and they look like this:

```<code>keys = [10200, 10201, 10202, ...]
</code>```

The URLs I want are of 3 types and they should look like these:

```<code>http://google.com/10200
http://bing.com/10200
http://yahoo.com/10200
</code>```

I am trying to loop through my keys[] and create 3 specific urls for each numerical keys that it contains. I have the following code where I am trying to:

read a numerical key from list --> create 3 URLs --> spit out a flow file.

...... and read the next numerical key in the list and keep looping.....

I have the following code but when I give it a JSON flowfile it does not do anything right now. Can someone please tell me what I am doing wrong?

```<code>import json
import java.io
from org.apache.commons.io import IOUtils
from java.nio.charset import StandardCharsets
from org.apache.nifi.processor.io import StreamCallback

class ModJSON(StreamCallback):

  def __init__(self):
        self.parentFlowFile = None
        pass
  def process(self, inputStream, outputStream):
    text = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
    obj = json.loads(text)
    flowfiles_list = []

    outputStream.write(bytearray(json.dumps(obj.keys(), indent=4).encode('utf-8')))

    for numerical_key in obj.keys():
      # create 1 flowfile for each numerical_key. Each flow file should have 3 url attributes
      flowFile = session.create(self.parentFlowFile)
      if (flowFile != None):
        flowFile = session.write(flowFile, "Does not matter")
        flowFile = session.putAttribute(flowFile, "google", "http://google.com/"+ numerical_key)

        flowFile = session.putAttribute(flowFile, "google", "http://bing.com/"+ numerical_key)

        flowFile = session.putAttribute(flowFile, "google", "http://yahoo.com/"+ numerical_key)
        flowfiles_list.append(flowFile)

    for flow in flowfiles_list:
      session.transfer(flow, REL_SUCCESS)
</code>```

答:
Good question, this is a nuance of the Callback approach to the flow file API. You've created a subclass of StreamCallback but you haven't retrieved an input flow file or used it to overwrite the content via an instance of your class.

Try this after the definition of your ModJSON class:

```<code>originalFlowFile = session.get()
if(originalFlowFile != None):
    originalFlowFile = session.write(flowFile, ModJSON())
    session.remove(originalFlowFile)
</code>```

This will get an input flow file (or wait for one to show up), then call your StreamCallback to overwrite the contents of your flow file. In my example you'd discard your input flow file, so if that's the right behavior for your use case, then instead you can just extend InputStreamCallback instead of StreamCallback and remove the outputStream.write(), if you are not using outputStream for anything. To do that, replace StreamCallback with InputStreamCallback and remove the "outputStream" parameter from the process() method.

In your example, once you add my snippet above, you are overwriting the input content with your json.dumps() command, as well as creating and transferring new files, all to the same relationship (success), so that could cause problems if they are not of the same format (which is why I added the session.remove()). If you need the original flow file to go out a different relationship than the rest, please consider InvokeScriptedProcessor rather than ExecuteScript. If you don't care about the input flow file after the processing (adding URL attributes is done), then follow my suggestion above. If they can all go out the same relationship (success), then replace my session.remove() with

```<code>session.transfer(originalFlowFile, REL_SUCCESS)
</code>```

Check my ExecuteScript cookbook article (part 2 of 3) for more examples of these use cases in Jython (and other languages) :)

Body:
I am working on a Nifi flow where I am getting a JSON document with multiple key-value pairs. I am using the ExecuteScript processor with python.

My goal here is to create various URLS bases on JSON keys. The keys are numerical and they look like this:

keys = [10200, 10201, 10202, ...]

The URLs I want are of 3 types and they should look like these:

http://google.com/10200
http://bing.com/10200
http://yahoo.com/10200

I am trying to loop through my keys[] and create 3 specific urls for each numerical keys that it contains. I have the following code where I am trying to:

read a numerical key from list --> create 3 URLs --> spit out a flow file.

...... and read the next numerical key in the list and keep looping.....

I have the following code but when I give it a JSON flowfile it does not do anything right now. Can someone please tell me what I am doing wrong?

import json
import java.io
from org.apache.commons.io import IOUtils
from java.nio.charset import StandardCharsets
from org.apache.nifi.processor.io import StreamCallback

class ModJSON(StreamCallback):

  def __init__(self):
        self.parentFlowFile = None
        pass
  def process(self, inputStream, outputStream):
    text = IOUtils.toString(inputStream, StandardCharsets.UTF_8)
    obj = json.loads(text)
    flowfiles_list = []

    outputStream.write(bytearray(json.dumps(obj.keys(), indent=4).encode('utf-8')))

    for numerical_key in obj.keys():
      # create 1 flowfile for each numerical_key. Each flow file should have 3 url attributes
      flowFile = session.create(self.parentFlowFile)
      if (flowFile != None):
        flowFile = session.write(flowFile, "Does not matter")
        flowFile = session.putAttribute(flowFile, "google", "http://google.com/"+ numerical_key)

        flowFile = session.putAttribute(flowFile, "google", "http://bing.com/"+ numerical_key)

        flowFile = session.putAttribute(flowFile, "google", "http://yahoo.com/"+ numerical_key)
        flowfiles_list.append(flowFile)

    for flow in flowfiles_list:
      session.transfer(flow, REL_SUCCESS)

Body:
Good question, this is a nuance of the Callback approach to the flow file API. You've created a subclass of StreamCallback but you haven't retrieved an input flow file or used it to overwrite the content via an instance of your class.

Try this after the definition of your ModJSON class:

originalFlowFile = session.get()
if(originalFlowFile != None):
    originalFlowFile = session.write(flowFile, ModJSON())
    session.remove(originalFlowFile)

This will get an input flow file (or wait for one to show up), then call your StreamCallback to overwrite the contents of your flow file. In my example you'd discard your input flow file, so if that's the right behavior for your use case, then instead you can just extend InputStreamCallback instead of StreamCallback and remove the outputStream.write(), if you are not using outputStream for anything. To do that, replace StreamCallback with InputStreamCallback and remove the "outputStream" parameter from the process() method.

In your example, once you add my snippet above, you are overwriting the input content with your json.dumps() command, as well as creating and transferring new files, all to the same relationship (success), so that could cause problems if they are not of the same format (which is why I added the session.remove()). If you need the original flow file to go out a different relationship than the rest, please consider InvokeScriptedProcessor rather than ExecuteScript. If you don't care about the input flow file after the processing (adding URL attributes is done), then follow my suggestion above. If they can all go out the same relationship (success), then replace my session.remove() with

session.transfer(originalFlowFile, REL_SUCCESS)

Check my ExecuteScript cookbook article (part 2 of 3) for more examples of these use cases in Jython (and other languages) :)


问:
reflection for orika customMap of different fieldNames
Suppose we have 2 DTOs:

```<code>public class ClassA {
    private String elementAClassA;
    private String elementBClassA;
    private Integer elementCClassA;
}

public class ClassB {
    private String elementAClassB;
    private String elementBClassB;
    private Integer elementCClassB;
}
</code>```

How to map these DTOs using generic code (or reflection/customised), ie without simply using:

```<code>MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
        mapperFactory.classMap(ClassA.class, ClassB.class)
                .field("elementAClassA", "elementAClassB")
                .field("elementBClassA", "elementBClassB")
                .field("elementCClassA", "elementCClassB").register();
</code>```

答:
If you're asking how to map common attributes from one representation (ClassA) to the other (ClassB), you would use the byDefault() as shown in the documentation.

```<code>mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class)
    .field("name", "fullName")
    .field("age", "currentAge")
    .byDefault()
    .register();
</code>```

Based on your example it isn't clear if the types are different or the names are different between your two representations (it isn't readable as Java).  Reflection would only be useful if the fields were named the same in which case you should just use byDefault(), or maybe if they were the same type and you only had one of each type in each representation you could kind of do an autowired-style mapping; but you would be making all sorts of assumptions you wouldn't want to make.

Body:
Suppose we have 2 DTOs:

public class ClassA {
    private String elementAClassA;
    private String elementBClassA;
    private Integer elementCClassA;
}

public class ClassB {
    private String elementAClassB;
    private String elementBClassB;
    private Integer elementCClassB;
}

How to map these DTOs using generic code (or reflection/customised), ie without simply using:

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
        mapperFactory.classMap(ClassA.class, ClassB.class)
                .field("elementAClassA", "elementAClassB")
                .field("elementBClassA", "elementBClassB")
                .field("elementCClassA", "elementCClassB").register();

Body:
If you're asking how to map common attributes from one representation (ClassA) to the other (ClassB), you would use the byDefault() as shown in the documentation.

mapperFactory.classMap(BasicPerson.class, BasicPersonDto.class)
    .field("name", "fullName")
    .field("age", "currentAge")
    .byDefault()
    .register();

Based on your example it isn't clear if the types are different or the names are different between your two representations (it isn't readable as Java).  Reflection would only be useful if the fields were named the same in which case you should just use byDefault(), or maybe if they were the same type and you only had one of each type in each representation you could kind of do an autowired-style mapping; but you would be making all sorts of assumptions you wouldn't want to make.


问:
How to clean a Service at the end of his use in Ionic / AngularJs
I have a service that looks like this (You can see it's from a shopping cart.)

I am trying to reset/remove this cart with a button (Buy More New Items"), to make a new order, but the quantity of the before order it continues, and whenever it makes a new buy order, sum with the previous order quantity.I am trying to reset/remove this cart with a button (Buy More New Items"), to make a new order, but the quantity of the before order it continues, and whenever it makes a new buy order, sum with the previous order quantity.

It's The Service so the correct is to say at the end of it uses:

```<code>    angular.module('nhaac.services', [])

.factory('sharedCartService', ['$ionicPopup',function($ionicPopup){

    // OKAY, MAS ONDE ELE ESTÁ PEGANDO?

    // DECLARA AS VARIÁVEIS

    var cartObj = {};
    cartObj.cart=[];
    cartObj.total_amount=0;
    cartObj.total_qty=0;

    // VERIFICA DE JÁ EXISTE ITENS NO CARRINHO
    cartObj.cart.add=function(id,image,name,price,qty,supply_id,deliver){
        if( cartObj.cart.find(id)!=-1  ){
            var alertPopup = $ionicPopup.alert({
                title: 'Este produto já foi Adicionado',
                template: 'Acrescente mais quantidade abaixo da oferta no carrinho'
            });

//        }if (CartObj, { 'cart_item_supply': supply_id}) {
        }if (cartObj.cart.findvovo(supply_id) !=-1 ){
            var alertPopup = $ionicPopup.alert({
               title: "Você só pode comprar de uma única Vovó por vez",
                template: "Volte ao carrinho e escolha a opção COMPRAR MAIS DELICIAS."
            });
        }else{
            cartObj.cart.push( { "cart_item_id": id , "cart_item_image": image , "cart_item_name": name , "cart_item_price": price , "cart_item_qty": qty, "cart_item_supply": supply_id, "cart_item_deliver": deliver  } );
            cartObj.total_qty+=1;
            cartObj.total_amount+=parseInt(price);
            console.log(cartObj);
        }
    };

    // PROCURA PRODUTOS NO CARRINHO PELO ID
    cartObj.cart.find=function(id){
        var result=-1;
        for( var i = 0, len = cartObj.cart.length; i < len; i++ ) {
            if( cartObj.cart[i].cart_item_id === id ) {
                result = i;
                console.log(result);
                break;
            }
        }
        return result;
    };

    // PROCURA COD FORNECEDOR É IGUAL
    cartObj.cart.findvovo=function(supply_id){
        var result=-1;
        for( var i = 0, len = cartObj.cart.length; i < len; i++ ) {
            if( cartObj.cart[i].cart_item_supply === supply_id ) {
                result = i;
               console.log('Qual Vovo achou '+result);
                break;
            }
    };
        return result;
    };

    // CLEAN CART
    cartObj.cart.drop=function(id){
     var temp=cartObj.cart[cartObj.cart.find(id)];
     cartObj.total_qty-= parseInt(temp.cart_item_qty);
     cartObj.total_amount-=( parseInt(temp.cart_item_qty) * parseInt(temp.cart_item_price) );
     window.localStorage.removeItem("fonecedor_carrinho",cartObj.supply_id);
     cartObj.cart.splice(cartObj.cart.find(id), 1);

    };

    // ADD ITENS TO CART
    cartObj.cart.increment=function(id){
         cartObj.cart[cartObj.cart.find(id)].cart_item_qty+=1;
         cartObj.total_qty+= 1;
         cartObj.total_amount+=( parseInt( cartObj.cart[cartObj.cart.find(id)].cart_item_price) );
    };

    // DIMINUI A QUANTIDADE DE ITENS NO CARRINHO
    cartObj.cart.decrement=function(id){

         cartObj.total_qty-= 1;
         cartObj.total_amount-= parseInt( cartObj.cart[cartObj.cart.find(id)].cart_item_price) ;

         if(cartObj.cart[cartObj.cart.find(id)].cart_item_qty == 1){  // if the cart item was only 1 in qty
            cartObj.cart.splice( cartObj.cart.find(id) , 1);  //edited
         }else{
            cartObj.cart[cartObj.cart.find(id)].cart_item_qty-=1;
         }

    };

    console.log('chama return');
    return cartObj;
}])
</code>```

You can see it's from a shopping cart.

I'm trying to delete this service like this:

```<code>$scope.novoPedido = function () {

            // CLEAN CART SHOP
            var cart = sharedCartService.cart;
            sharedCartService.cart.splice(0, sharedCartService.cart.length);

          //  sharedCartService.cart.drop("");
 >> This dont work

  //          $state.go('novopedido');

        };
</code>```

But it does not delete the quantity, it just clears the items, the quantity continues, how to delete everything, including the quantity? I do not know how to do it ...

Thank you!

答:
Resolved with:</p>

```<code>var cart = sharedCartService.cart;
        sharedCartService.cart.splice(0, sharedCartService.cart.length);

        sharedCartService.total_qty = 0;
        sharedCartService.total_amount = 0;
</code>```

Body:
I have a service that looks like this (You can see it's from a shopping cart.)

I am trying to reset/remove this cart with a button (Buy More New Items"), to make a new order, but the quantity of the before order it continues, and whenever it makes a new buy order, sum with the previous order quantity.I am trying to reset/remove this cart with a button (Buy More New Items"), to make a new order, but the quantity of the before order it continues, and whenever it makes a new buy order, sum with the previous order quantity.

It's The Service so the correct is to say at the end of it uses:

    angular.module('nhaac.services', [])

.factory('sharedCartService', ['$ionicPopup',function($ionicPopup){

    // OKAY, MAS ONDE ELE ESTÁ PEGANDO?

    // DECLARA AS VARIÁVEIS

    var cartObj = {};
    cartObj.cart=[];
    cartObj.total_amount=0;
    cartObj.total_qty=0;

    // VERIFICA DE JÁ EXISTE ITENS NO CARRINHO
    cartObj.cart.add=function(id,image,name,price,qty,supply_id,deliver){
        if( cartObj.cart.find(id)!=-1  ){
            var alertPopup = $ionicPopup.alert({
                title: 'Este produto já foi Adicionado',
                template: 'Acrescente mais quantidade abaixo da oferta no carrinho'
            });

//        }if (CartObj, { 'cart_item_supply': supply_id}) {
        }if (cartObj.cart.findvovo(supply_id) !=-1 ){
            var alertPopup = $ionicPopup.alert({
               title: "Você só pode comprar de uma única Vovó por vez",
                template: "Volte ao carrinho e escolha a opção COMPRAR MAIS DELICIAS."
            });
        }else{
            cartObj.cart.push( { "cart_item_id": id , "cart_item_image": image , "cart_item_name": name , "cart_item_price": price , "cart_item_qty": qty, "cart_item_supply": supply_id, "cart_item_deliver": deliver  } );
            cartObj.total_qty+=1;
            cartObj.total_amount+=parseInt(price);
            console.log(cartObj);
        }
    };

    // PROCURA PRODUTOS NO CARRINHO PELO ID
    cartObj.cart.find=function(id){
        var result=-1;
        for( var i = 0, len = cartObj.cart.length; i < len; i++ ) {
            if( cartObj.cart[i].cart_item_id === id ) {
                result = i;
                console.log(result);
                break;
            }
        }
        return result;
    };

    // PROCURA COD FORNECEDOR É IGUAL
    cartObj.cart.findvovo=function(supply_id){
        var result=-1;
        for( var i = 0, len = cartObj.cart.length; i < len; i++ ) {
            if( cartObj.cart[i].cart_item_supply === supply_id ) {
                result = i;
               console.log('Qual Vovo achou '+result);
                break;
            }
    };
        return result;
    };

    // CLEAN CART
    cartObj.cart.drop=function(id){
     var temp=cartObj.cart[cartObj.cart.find(id)];
     cartObj.total_qty-= parseInt(temp.cart_item_qty);
     cartObj.total_amount-=( parseInt(temp.cart_item_qty) * parseInt(temp.cart_item_price) );
     window.localStorage.removeItem("fonecedor_carrinho",cartObj.supply_id);
     cartObj.cart.splice(cartObj.cart.find(id), 1);

    };

    // ADD ITENS TO CART
    cartObj.cart.increment=function(id){
         cartObj.cart[cartObj.cart.find(id)].cart_item_qty+=1;
         cartObj.total_qty+= 1;
         cartObj.total_amount+=( parseInt( cartObj.cart[cartObj.cart.find(id)].cart_item_price) );
    };

    // DIMINUI A QUANTIDADE DE ITENS NO CARRINHO
    cartObj.cart.decrement=function(id){

         cartObj.total_qty-= 1;
         cartObj.total_amount-= parseInt( cartObj.cart[cartObj.cart.find(id)].cart_item_price) ;

         if(cartObj.cart[cartObj.cart.find(id)].cart_item_qty == 1){  // if the cart item was only 1 in qty
            cartObj.cart.splice( cartObj.cart.find(id) , 1);  //edited
         }else{
            cartObj.cart[cartObj.cart.find(id)].cart_item_qty-=1;
         }

    };

    console.log('chama return');
    return cartObj;
}])

You can see it's from a shopping cart.

I'm trying to delete this service like this:

$scope.novoPedido = function () {

            // CLEAN CART SHOP
            var cart = sharedCartService.cart;
            sharedCartService.cart.splice(0, sharedCartService.cart.length);

          //  sharedCartService.cart.drop("");
 >> This dont work

  //          $state.go('novopedido');

        };

But it does not delete the quantity, it just clears the items, the quantity continues, how to delete everything, including the quantity? I do not know how to do it ...

Thank you!

Body:
Resolved with:

var cart = sharedCartService.cart;
        sharedCartService.cart.splice(0, sharedCartService.cart.length);

        sharedCartService.total_qty = 0;
        sharedCartService.total_amount = 0;


问:
How to do POST and GET request same button jQuery
I have been stuck on this same problem now for 2 weeks with 10-15 hours a day.

I am using dynamic buttons that append more buttons which work like tabs on my view which are dynamic.

I can only use 1 form for this so the dynamic buttons are being used to know which model ID to load.

I have to use only 1 form Simple jquery is this:

```<code>$("#copy-link").click(function (e) {
    e.preventDefault();

    var num_tabs = $("div#tabSequence ul li").length + 1;
    $("div#tabSequence ul").append(
        "<li class='tab-button'><a data-toggle='tab' id='link" + num_tabs + "' href='#tab" + num_tabs + "'>#" + num_tabs + "</a></li>"
    );

    var data =  $('#campaign-form').serialize();

      $.ajax(
            {
                dataType: 'html',
                type: 'POST',
                url: "campaigns/sequencesave",
                data: data,
                success: function(response){
                    var campaignId = response;
                    var test2 = document.getElementById("link" + num_tabs);
                    console.log(test2);
                    test2.setAttribute("data-campaign", campaignId);
                }

            }
    )
});
</code>```

Is there a way I can POST form data and also load form content for the Tab button clicked?

```<code>$("body").on("click", ".nav-tabs li a", function() {
        var data =  $('#campaign-form').serialize();

        $.ajax(
            {
                dataType: 'html',
                type: 'POST',
                url: "campaigns/sequenceupdate",
                data: { 'campaign_uid' : $(this).attr('data-campaign'), csrf_token: csrfTokenValue },

            }
        )
    })
</code>```

^^ that same button clicked has to save what is currently on the view and then load contents for the button clicked. I am appending an ID of the Model needed to the button when user clicks #copy-link and saving it automatically

答:
Im not a jQuery wiz but you can do this with straight up javascript in conjunction with Jquery.

```<code>function postInfo(event){
    httpRequest = new XMLHttpRequest();
    if (!httpRequest) {
      alert('Giving up :( Cannot create an XMLHTTP instance');
      return false;
    }
    httpRequest.onreadystatechange = results;
    httpRequest.open('POST', 'ENTER YOUR URL HERE');
    httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send('serverpin='+ServerPIN+'&restuarantid='+RestaurantID); //<-these are the variables you are posting
  }

function results(){
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
      if (httpRequest.status === 200) {
        // WHATEVER YOU WANT TO MAKE IT DO IF THE REQUEST IS SUCCESSFUL
      } else {
        alert('There was a problem with the request.');
      }
    }
  }
</code>```

And then to invoke it I use an event listener like this

```<code>document.getElementById('WHATEVERIDMATTERS').addEventListener('click',postOrder);
</code>```

Body:
I have been stuck on this same problem now for 2 weeks with 10-15 hours a day.

I am using dynamic buttons that append more buttons which work like tabs on my view which are dynamic.

I can only use 1 form for this so the dynamic buttons are being used to know which model ID to load.

I have to use only 1 form Simple jquery is this:

$("#copy-link").click(function (e) {
    e.preventDefault();

    var num_tabs = $("div#tabSequence ul li").length + 1;
    $("div#tabSequence ul").append(
        "
#" + num_tabs + "
"
    );

    var data =  $('#campaign-form').serialize();

      $.ajax(
            {
                dataType: 'html',
                type: 'POST',
                url: "campaigns/sequencesave",
                data: data,
                success: function(response){
                    var campaignId = response;
                    var test2 = document.getElementById("link" + num_tabs);
                    console.log(test2);
                    test2.setAttribute("data-campaign", campaignId);
                }

            }
    )
});

Is there a way I can POST form data and also load form content for the Tab button clicked?

$("body").on("click", ".nav-tabs li a", function() {
        var data =  $('#campaign-form').serialize();

        $.ajax(
            {
                dataType: 'html',
                type: 'POST',
                url: "campaigns/sequenceupdate",
                data: { 'campaign_uid' : $(this).attr('data-campaign'), csrf_token: csrfTokenValue },

            }
        )
    })

^^ that same button clicked has to save what is currently on the view and then load contents for the button clicked. I am appending an ID of the Model needed to the button when user clicks #copy-link and saving it automatically

Body:
Im not a jQuery wiz but you can do this with straight up javascript in conjunction with Jquery.

function postInfo(event){
    httpRequest = new XMLHttpRequest();
    if (!httpRequest) {
      alert('Giving up :( Cannot create an XMLHTTP instance');
      return false;
    }
    httpRequest.onreadystatechange = results;
    httpRequest.open('POST', 'ENTER YOUR URL HERE');
    httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send('serverpin='+ServerPIN+'&restuarantid='+RestaurantID); //<-these are the variables you are posting
  }

function results(){
    if (httpRequest.readyState === XMLHttpRequest.DONE) {
      if (httpRequest.status === 200) {
        // WHATEVER YOU WANT TO MAKE IT DO IF THE REQUEST IS SUCCESSFUL
      } else {
        alert('There was a problem with the request.');
      }
    }
  }

And then to invoke it I use an event listener like this

document.getElementById('WHATEVERIDMATTERS').addEventListener('click',postOrder);


问:
Android - How to create this layout background (image inside)
How can I create this kind of background on Android? I have no idea how to do this.

This is what I've tried

triangle.xml

```<code><layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <rotate
        android:fromDegrees="-77"
        android:pivotX="0"
        android:pivotY="0"
        android:toDegrees="0">
        <shape android:shape="rectangle">
            <solid android:color="@color/colorPrimary" />
        </shape>
    </rotate>
</item>
</layer-list>
</code>```

layout.xml

```<code><?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="br.com.fornaro.armariovirtual.AccountCreatedActivity">

<View
    android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:background="@color/colorPrimary"
    android:elevation="@dimen/elevation" />

<View
    android:id="@+id/view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/background"
    android:background="@drawable/triangle"
    android:elevation="@dimen/elevation" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@+id/view"
    android:layout_centerHorizontal="true"
    android:elevation="@dimen/elevation"
    android:gravity="center"
    android:text="@string/account_created"
    android:textColor="@android:color/white"
    android:textSize="30sp" />

<Button
    style="@style/Widget.AppCompat.Button.Borderless"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true"
    android:layout_marginBottom="16dp"
    android:layout_marginEnd="16dp"
    android:layout_marginRight="16dp"
    android:drawableEnd="@android:drawable/ic_media_play"
    android:drawableRight="@android:drawable/ic_media_play"
    android:drawableTint="@color/colorPrimary"
    android:text="@string/continue_label"
    android:textColor="@color/colorPrimary"
    android:textStyle="bold" />

</RelativeLayout>
</code>```

The result is:

How to fix this triangle? Or do this layout using a better way?

答: Use Paint or PhotoShop to do is the easiest way.</p>

Body:
How can I create this kind of background on Android? I have no idea how to do this.

This is what I've tried

triangle.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <rotate
        android:fromDegrees="-77"
        android:pivotX="0"
        android:pivotY="0"
        android:toDegrees="0">
        <shape android:shape="rectangle">
            <solid android:color="@color/colorPrimary" />
        </shape>
    </rotate>
</item>
</layer-list>

layout.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="br.com.fornaro.armariovirtual.AccountCreatedActivity">

<View
    android:id="@+id/background"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    android:background="@color/colorPrimary"
    android:elevation="@dimen/elevation" />

<View
    android:id="@+id/view"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/background"
    android:background="@drawable/triangle"
    android:elevation="@dimen/elevation" />

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_above="@+id/view"
    android:layout_centerHorizontal="true"
    android:elevation="@dimen/elevation"
    android:gravity="center"
    android:text="@string/account_created"
    android:textColor="@android:color/white"
    android:textSize="30sp" />

<Button
    style="@style/Widget.AppCompat.Button.Borderless"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    android:layout_alignParentEnd="true"
    android:layout_alignParentRight="true"
    android:layout_marginBottom="16dp"
    android:layout_marginEnd="16dp"
    android:layout_marginRight="16dp"
    android:drawableEnd="@android:drawable/ic_media_play"
    android:drawableRight="@android:drawable/ic_media_play"
    android:drawableTint="@color/colorPrimary"
    android:text="@string/continue_label"
    android:textColor="@color/colorPrimary"
    android:textStyle="bold" />

</RelativeLayout>

The result is:

How to fix this triangle? Or do this layout using a better way?


问:
How to use "literal string tokens" in Bison
I am learning Flex/Bison. The manual of Bison says:

A literal string token is written like a C string constant; for
  example, "<=" is a literal string token. A literal string token
  doesn’t need to be declared unless you need to specify its semantic
  value data type

But I do not figure how to use it and I do not find an example.

I have the following code for testing:

example.l

```<code>%option noyywrap nodefault

%{
#include "example.tab.h"
%}

%%

[ \t\n] {;}
[0-9] { return NUMBER; }
. { return yytext[0]; }

%%
</code>```

example.y

```<code>%{
#include <stdio.h>
#define YYSTYPE char *
%}

%token NUMBER

%%

start: %empty | start tokens

tokens:
       NUMBER "<=" NUMBER { printf("<="); }
     | NUMBER "=>" NUMBER { printf("=>\n"); }
     | NUMBER '>' NUMBER { printf(">\n"); }
     | NUMBER '<' NUMBER { printf("<\n"); }

%%

main(int argc, char **argv) {
   yyparse();
}

yyerror(char *s) {
   fprintf(stderr, "error: %s\n", s);
}
</code>```

Makefile

```<code>#!/usr/bin/make
# by RAM

all: example

example.tab.c example.tab.h: example.y
    bison -d $<

lex.yy.c: example.l example.tab.h
    flex $<

example: lex.yy.c example.tab.c
    cc -o $@ example.tab.c lex.yy.c -lfl

clean:
    rm -fr example.tab.c example.tab.h lex.yy.c example
</code>```

And when I run it:

```<code>$ ./example
3<4
<
6>9
>
6=>9
error: syntax error
</code>```

Any idea?

UPDATE: I want to clarify that I know alternative ways to solve it, but I want to use literal string tokens.

One Alternative: using multiple "literal character tokens":

```<code>tokens:
       NUMBER '<' '=' NUMBER { printf("<="); }
     | NUMBER '=' '>' NUMBER { printf("=>\n"); }
     | NUMBER '>' NUMBER { printf(">\n"); }
     | NUMBER '<' NUMBER { printf("<\n"); }
</code>```

When I run it:

```<code>$ ./example
3<=9
<=
</code>```

Other alternative:

In example.l:

```<code>"<="  { return LE; }
"=>"  { return GE; }
</code>```

In example.y:

```<code>...
%token NUMBER
%token LE "<="
%token GE "=>"

%%

start: %empty | start tokens

tokens:
       NUMBER "<=" NUMBER { printf("<="); }
     | NUMBER "=>" NUMBER { printf("=>\n"); }
     | NUMBER '>' NUMBER { printf(">\n"); }
     | NUMBER '<' NUMBER { printf("<\n"); }
...
</code>```

When I run it:

```<code>$ ./example
3<=4
<=
</code>```

But the manual says:

A literal string token doesn’t need to be declared unless you need to
  specify its semantic value data type

答:
The quoted manual paragraph is correct, but you need to read the next paragraph, too:

You can associate the literal string token with a symbolic name as an alias, using the %token declaration (see Token Declarations). If you don’t do that, the lexical analyzer has to retrieve the token number for the literal string token from the yytname table.

So you don't need to declare the literal string token, but you still need to arrange for the lexer to send the correct token number, and if you don't declare an associated token name, the only way to find the correct value is to search for the code in the yytname table.

In short, your last example where you define LE and GE as aliases, is by far the most common approach. Separating the tokens into individual characters is not a good idea; it might create shift-reduce conflicts and it will definitely allow invalid inputs, such as putting whitespace between the characters.

If you want to try the yytname solution, there is sample code in the bison manual. But please be aware that this code discovers bison's internal token number, which is not the number which needs to be returned from the scanner. There is no way to get the external token number which is easy, portable and documented; the easy and undocumented way is to look the token number up in yytoknum but since that array is not documented and conditional on a preprocessor macro, there is no guarantee that it will work. Note also that these tables are declared static so the function(s) which rely on them must be included in the bison input file. (Of course, these functions can have external linkage so that they can be called from the lexer. But you can't just use yytname directly in the lexer.)

Body:
I am learning Flex/Bison. The manual of Bison says:

A literal string token is written like a C string constant; for
  example, "<=" is a literal string token. A literal string token
  doesn’t need to be declared unless you need to specify its semantic
  value data type

But I do not figure how to use it and I do not find an example.

I have the following code for testing:

example.l

%option noyywrap nodefault

%{
#include "example.tab.h"
%}

%%

[ \t\n] {;}
[0-9] { return NUMBER; }
. { return yytext[0]; }

%%

example.y

%{
#include <stdio.h>
#define YYSTYPE char *
%}

%token NUMBER

%%

start: %empty | start tokens

tokens:
       NUMBER "<=" NUMBER { printf("<="); }
     | NUMBER "=>" NUMBER { printf("=>\n"); }
     | NUMBER '>' NUMBER { printf(">\n"); }
     | NUMBER '<' NUMBER { printf("<\n"); }

%%

main(int argc, char **argv) {
   yyparse();
}

yyerror(char *s) {
   fprintf(stderr, "error: %s\n", s);
}

Makefile

#!/usr/bin/make
# by RAM

all: example

example.tab.c example.tab.h: example.y
    bison -d $<

lex.yy.c: example.l example.tab.h
    flex $<

example: lex.yy.c example.tab.c
    cc -o $@ example.tab.c lex.yy.c -lfl

clean:
    rm -fr example.tab.c example.tab.h lex.yy.c example

And when I run it:

$ ./example
3<4
<
6>9
>
6=>9
error: syntax error

Any idea?

UPDATE: I want to clarify that I know alternative ways to solve it, but I want to use literal string tokens.

One Alternative: using multiple "literal character tokens":

tokens:
       NUMBER '<' '=' NUMBER { printf("<="); }
     | NUMBER '=' '>' NUMBER { printf("=>\n"); }
     | NUMBER '>' NUMBER { printf(">\n"); }
     | NUMBER '<' NUMBER { printf("<\n"); }

When I run it:

$ ./example
3<=9
<=

Other alternative:

In example.l:

"<="  { return LE; }
"=>"  { return GE; }

In example.y:

...
%token NUMBER
%token LE "<="
%token GE "=>"

%%

start: %empty | start tokens

tokens:
       NUMBER "<=" NUMBER { printf("<="); }
     | NUMBER "=>" NUMBER { printf("=>\n"); }
     | NUMBER '>' NUMBER { printf(">\n"); }
     | NUMBER '<' NUMBER { printf("<\n"); }
...

When I run it:

$ ./example
3<=4
<=

But the manual says:

A literal string token doesn’t need to be declared unless you need to
  specify its semantic value data type

ContentLicense: CC BY-SA 4.0


问:
How to add one day to a date?
I want to add one day to a particular date. How can I do that?

```<code>Date dt = new Date();
</code>```

Now I want to add one day to this date.

答:
Java 8 LocalDate API

LocalDate.now().plusDays(1L);

ContentLicense: CC BY-SA 2.5

Title: How to add one day to a date?

Body:
I want to add one day to a particular date. How can I do that?

Date dt = new Date();

Now I want to add one day to this date.


问:
Installation Tools for ASP.Net C# application
We have developed an ASP.Net/C#/SQL Server application and use SetupBuilder (from LinderSoft) and MSI for software installation purposes.

We are having all sorts trouble with the MSI component - in that installations regularly fail and we appear to have limited control over the MSI interface/process.

Are there any installation tools that give us more control over the MSI install process and can anyone recommend a better set of tools for software deployment purposes?

答:
WIX provides a lot of control in creating MSI's.
Takes a bit of learning but we have used it for creating MSI's in our projects and it has worked for all of our needs.

From their site:

The Windows Installer XML (WiX) is a toolset that builds Windows
  installation
      packages from XML source code. The toolset supports a command line
      environment that developers may integrate into their build processes
      to build MSI and MSM setup packages

Sorry for the generic answer, but if you could provide more details regarding the errors that you are facing or exactly what areas you want to be able to control, then might be able to throw some light on it.